Skip to content

Commit

Permalink
more work on testing null-value alternatives (skip, fail, set-as-empty)
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Feb 9, 2017
1 parent 6636ea2 commit 0baca76
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 99 deletions.
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ public PropertyMetadata withMergeInfo(MergeInfo mergeInfo) {
/** /**
* @since 2.9 * @since 2.9
*/ */
public PropertyMetadata withNulls(JsonSetter.Nulls valueNulls, JsonSetter.Nulls contentNulls) { public PropertyMetadata withNulls(JsonSetter.Nulls valueNulls,
JsonSetter.Nulls contentNulls) {
return new PropertyMetadata(_required, _description, _index, _defaultValue, return new PropertyMetadata(_required, _description, _index, _defaultValue,
_mergeInfo, valueNulls, contentNulls); _mergeInfo, valueNulls, contentNulls);
} }
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1140,6 +1140,9 @@ protected final NullValueProvider _findNullProvider(DeserializationContext ctxt,
if (nulls != null) { if (nulls != null) {
switch (nulls) { switch (nulls) {
case FAIL: case FAIL:
if (prop == null) {
return NullsFailProvider.constructForRootValue(ctxt.constructType(valueDeser.handledType()));
}
return NullsFailProvider.constructForProperty(prop); return NullsFailProvider.constructForProperty(prop);
case AS_EMPTY: case AS_EMPTY:
// can not deal with empty values if there is no value deserializer that // can not deal with empty values if there is no value deserializer that
Expand Down Expand Up @@ -1197,7 +1200,8 @@ protected final NullValueProvider _findNullProvider(DeserializationContext ctxt,
* If null, will assume type is what {@link #getValueClass} returns. * If null, will assume type is what {@link #getValueClass} returns.
* @param propName Name of the property that can not be mapped * @param propName Name of the property that can not be mapped
*/ */
protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt, Object instanceOrClass, String propName) protected void handleUnknownProperty(JsonParser p, DeserializationContext ctxt,
Object instanceOrClass, String propName)
throws IOException throws IOException
{ {
if (instanceOrClass == null) { if (instanceOrClass == null) {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ protected PropertyMetadata _getSetterInfo(PropertyMetadata metadata)
// If not, config override? // If not, config override?
// 25-Oct-2016, tatu: Either this, or type of accessor... // 25-Oct-2016, tatu: Either this, or type of accessor...
if (needMerge || (valueNulls == null) || (contentNulls == null)) { if (needMerge || (valueNulls == null) || (contentNulls == null)) {
Class<?> rawType = (acc == null) ? getRawPrimaryType() : acc.getRawType(); // NOTE: "acc.getRawClass()" does NOT work as well, prone to type erasure
Class<?> rawType = (acc == null) ? getRawPrimaryType() : acc.getType().getRawClass();
ConfigOverride co = _config.getConfigOverride(rawType); ConfigOverride co = _config.getConfigOverride(rawType);
JsonSetter.Value setterInfo = co.getSetterInfo(); JsonSetter.Value setterInfo = co.getSetterInfo();
if (setterInfo != null) { if (setterInfo != null) {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void testThatJsonIgnoreWorksWithConstructorProperties() throws Exception
Testing testing = new Testing("shouldBeIgnored", "notIgnore"); Testing testing = new Testing("shouldBeIgnored", "notIgnore");
ObjectMapper om = new ObjectMapper(); ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(testing); String json = om.writeValueAsString(testing);
System.out.println(json); // System.out.println(json);
assertFalse(json.contains("shouldBeIgnored")); assertFalse(json.contains("shouldBeIgnored"));
} }
} }
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.*; import java.util.*;


import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonSetter.Nulls;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidNullException; import com.fasterxml.jackson.databind.exc.InvalidNullException;
Expand Down Expand Up @@ -41,7 +42,7 @@ static class NullContentUndefined<T> {
/********************************************************** /**********************************************************
*/ */


// Tests to verify that we can set default settings // Tests to verify that we can set default settings for failure
public void testFailOnNullFromDefaults() throws Exception public void testFailOnNullFromDefaults() throws Exception
{ {
final String JSON = aposToQuotes("{'values':[null]}"); final String JSON = aposToQuotes("{'values':[null]}");
Expand All @@ -52,6 +53,27 @@ public void testFailOnNullFromDefaults() throws Exception
assertNotNull(result.values); assertNotNull(result.values);
assertEquals(1, result.values.size()); assertEquals(1, result.values.size());
assertNull(result.values.get(0)); assertNull(result.values.get(0));

// but not when overridden globally:
ObjectMapper mapper = new ObjectMapper();
mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL));
try {
mapper.readValue(JSON, listType);
fail("Should not pass");
} catch (InvalidNullException e) {
verifyException(e, "property \"values\"");
}

// or configured for type:
mapper = new ObjectMapper();
mapper.configOverride(List.class)
.setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL));
try {
mapper.readValue(JSON, listType);
fail("Should not pass");
} catch (InvalidNullException e) {
verifyException(e, "property \"values\"");
}
} }


public void testFailOnNullWithCollections() throws Exception public void testFailOnNullWithCollections() throws Exception
Expand Down Expand Up @@ -159,7 +181,7 @@ public void testFailOnNullWithMaps() throws Exception
/********************************************************** /**********************************************************
*/ */


public void testNullsAsEmpty() throws Exception public void testNullsAsEmptyWithCollections() throws Exception
{ {
final String JSON = aposToQuotes("{'values':[null]}"); final String JSON = aposToQuotes("{'values':[null]}");


Expand All @@ -180,6 +202,27 @@ public void testNullsAsEmpty() throws Exception
} }
} }


public void testNullsAsEmptyUsingDefaults() throws Exception
{
final String JSON = aposToQuotes("{'values':[null]}");
TypeReference<?> listType = new TypeReference<NullContentUndefined<List<Integer>>>() { };

// Let's see defaulting in action
ObjectMapper mapper = new ObjectMapper();
mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY));
NullContentUndefined<List<Integer>> result = mapper.readValue(JSON, listType);
assertEquals(1, result.values.size());
assertEquals(Integer.valueOf(0), result.values.get(0));

// or configured for type:
mapper = new ObjectMapper();
mapper.configOverride(List.class)
.setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY));
result = mapper.readValue(JSON, listType);
assertEquals(1, result.values.size());
assertEquals(Integer.valueOf(0), result.values.get(0));
}

public void testNullsAsEmptyWithArrays() throws Exception public void testNullsAsEmptyWithArrays() throws Exception
{ {
// Note: skip `Object[]`, no default empty value at this point // Note: skip `Object[]`, no default empty value at this point
Expand Down Expand Up @@ -251,7 +294,46 @@ public void testNullsAsEmptyWithMaps() throws Exception
/********************************************************** /**********************************************************
*/ */


public void testNullsSkip() throws Exception public void testNullsSkipUsingDefaults() throws Exception
{
final String JSON = aposToQuotes("{'values':[null]}");
TypeReference<?> listType = new TypeReference<NullContentUndefined<List<Long>>>() { };

// Let's see defaulting in action
ObjectMapper mapper = new ObjectMapper();
mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP));
NullContentUndefined<List<Long>> result = mapper.readValue(JSON, listType);
assertEquals(0, result.values.size());

// or configured for type:
mapper = new ObjectMapper();
mapper.configOverride(List.class)
.setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP));
result = mapper.readValue(JSON, listType);
assertEquals(0, result.values.size());
}

// Test to verify that per-property setting overrides defaults:
public void testNullsSkipWithOverrides() throws Exception
{
final String JSON = aposToQuotes("{'values':[null]}");
TypeReference<?> listType = new TypeReference<NullContentSkip<List<Long>>>() { };

ObjectMapper mapper = new ObjectMapper();
// defaults call for fail; but POJO specifies "skip"; latter should win
mapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL));
NullContentSkip<List<Long>> result = mapper.readValue(JSON, listType);
assertEquals(0, result.values.size());

// ditto for per-type defaults
mapper = new ObjectMapper();
mapper.configOverride(List.class)
.setSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL));
result = mapper.readValue(JSON, listType);
assertEquals(0, result.values.size());
}

public void testNullsSkipWithCollections() throws Exception
{ {
// List<Integer> // List<Integer>
{ {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,46 +4,17 @@
import java.util.Map; import java.util.Map;


import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonSetter.Nulls;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;

import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidNullException;


// for [databind#1402]; configurable null handling, for values themselves // for [databind#1402]; configurable null handling, for values themselves,
public class NullConversionsTest extends BaseMapTest // using generic types
public class NullConversionsGenericTest extends BaseMapTest
{ {
static class NullFail {
public String nullsOk = "a";

@JsonSetter(nulls=JsonSetter.Nulls.FAIL)
public String noNulls = "b";
}

static class NullAsEmpty {
public String nullsOk = "a";

@JsonSetter(nulls=JsonSetter.Nulls.AS_EMPTY)
public String nullAsEmpty = "b";
}

static class NullsForString {
/*
String n = "foo";
public void setName(String name) {
n = name;
}
*/

String n = "foo";

public void setName(String n0) { n = n0; }
public String getName() { return n; }
}

static class GeneralEmpty<T> { static class GeneralEmpty<T> {
@JsonSetter(nulls=JsonSetter.Nulls.AS_EMPTY) @JsonSetter(nulls=JsonSetter.Nulls.AS_EMPTY)
public T value; T value;


@JsonSetter(nulls=JsonSetter.Nulls.AS_EMPTY) @JsonSetter(nulls=JsonSetter.Nulls.AS_EMPTY)
public void setValue(T v) { public void setValue(T v) {
Expand All @@ -59,7 +30,7 @@ static class NoCtorWrapper {
static class NoCtorPOJO { static class NoCtorPOJO {
public NoCtorPOJO(boolean b) { } public NoCtorPOJO(boolean b) { }
} }

/* /*
/********************************************************** /**********************************************************
/* Test methods /* Test methods
Expand All @@ -68,63 +39,6 @@ public NoCtorPOJO(boolean b) { }


private final ObjectMapper MAPPER = new ObjectMapper(); private final ObjectMapper MAPPER = new ObjectMapper();


public void testFailOnNull() throws Exception
{
// first, ok if assigning non-null to not-nullable, null for nullable
NullFail result = MAPPER.readValue(aposToQuotes("{'noNulls':'foo', 'nullsOk':null}"),
NullFail.class);
assertEquals("foo", result.noNulls);
assertNull(result.nullsOk);

// and then see that nulls are not ok for non-nullable
try {
result = MAPPER.readValue(aposToQuotes("{'noNulls':null}"),
NullFail.class);
fail("Should not pass");
} catch (InvalidNullException e) {
verifyException(e, "property \"noNulls\"");
}

// also: config overrides by type should work
String json = aposToQuotes("{'name':null}");
NullsForString def = MAPPER.readValue(json, NullsForString.class);
assertNull(def.getName());

ObjectMapper mapper = new ObjectMapper();
mapper.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.FAIL));
try {
mapper.readValue(json, NullsForString.class);
fail("Should not pass");
} catch (InvalidNullException e) {
verifyException(e, "property \"name\"");
}
}

public void testNullsToEmptyScalar() throws Exception
{
NullAsEmpty result = MAPPER.readValue(aposToQuotes("{'nullAsEmpty':'foo', 'nullsOk':null}"),
NullAsEmpty.class);
assertEquals("foo", result.nullAsEmpty);
assertNull(result.nullsOk);

// and then see that nulls are not ok for non-nullable
result = MAPPER.readValue(aposToQuotes("{'nullAsEmpty':null}"),
NullAsEmpty.class);
assertEquals("", result.nullAsEmpty);

// also: config overrides by type should work
String json = aposToQuotes("{'name':null}");
NullsForString def = MAPPER.readValue(json, NullsForString.class);
assertNull(def.getName());

ObjectMapper mapper = new ObjectMapper();
mapper.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
NullsForString named = mapper.readValue(json, NullsForString.class);
assertEquals("", named.getName());
}

public void testNullsToEmptyPojo() throws Exception public void testNullsToEmptyPojo() throws Exception
{ {
GeneralEmpty<Point> result = MAPPER.readValue(aposToQuotes("{'value':null}"), GeneralEmpty<Point> result = MAPPER.readValue(aposToQuotes("{'value':null}"),
Expand Down
Loading

0 comments on commit 0baca76

Please sign in to comment.