Skip to content

Commit 4033c89

Browse files
committed
Add support "skip nulls" for content values.
1 parent 0ec28dd commit 4033c89

12 files changed

+255
-87
lines changed

src/main/java/com/fasterxml/jackson/databind/deser/NullValueProvider.java

-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@
1515
*/
1616
public interface NullValueProvider
1717
{
18-
/**
19-
* Marker object used in some context to mark return value that is to indicate
20-
* that `null` should be skipped (instead of being replaced).
21-
*/
22-
public final static Object SKIP_MARKER = new Object();
23-
2418
/**
2519
* Method called to possibly convert incoming `null` token (read via
2620
* underlying streaming input source) into other value of type accessor

src/main/java/com/fasterxml/jackson/databind/deser/impl/NullsConstantProvider.java

+23-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class NullsConstantProvider
1414
{
1515
private static final long serialVersionUID = 1L;
1616

17-
private final static NullsConstantProvider SKIPPER = new NullsConstantProvider(NullValueProvider.SKIP_MARKER);
17+
private final static NullsConstantProvider SKIPPER = new NullsConstantProvider(null);
1818

1919
private final static NullsConstantProvider NULLER = new NullsConstantProvider(null);
2020

@@ -29,9 +29,10 @@ protected NullsConstantProvider(Object nvl) {
2929
}
3030

3131
/**
32-
* Static accessor for a stateless instance that always returns
33-
* {@link NullValueProvider#SKIP_MARKER} marker instance, used to indicate
34-
* that null value is to be skipped instead of replacing it.
32+
* Static accessor for a stateless instance used as marker, to indicate
33+
* that all input `null` values should be skipped (ignored), so that
34+
* no corresponding property value is set (with POJOs), and no content
35+
* values (array/Collection elements, Map entries) are added.
3536
*/
3637
public static NullsConstantProvider skipper() {
3738
return SKIPPER;
@@ -48,6 +49,24 @@ public static NullsConstantProvider forValue(Object nvl) {
4849
return new NullsConstantProvider(nvl);
4950
}
5051

52+
/**
53+
* Utility method that can be used to check if given null value provider
54+
* is "skipper", marker provider that means that all input `null`s should
55+
* be skipped (ignored), instead of converted
56+
*/
57+
public static boolean isSkipper(NullValueProvider p) {
58+
return (p == SKIPPER);
59+
}
60+
61+
/**
62+
* Utility method that can be used to check if given null value provider
63+
* is "nuller", no-operation provider that will always simply return
64+
* Java `null` for any and all input `null`s.
65+
*/
66+
public static boolean isNuller(NullValueProvider p) {
67+
return (p == NULLER);
68+
}
69+
5170
@Override
5271
public AccessPattern getNullAccessPattern() {
5372
return _access;

src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,15 @@ public CollectionDeserializer createContextual(DeserializationContext ctxt,
188188
if (valueTypeDeser != null) {
189189
valueTypeDeser = valueTypeDeser.forProperty(property);
190190
}
191+
NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);
191192
if ( (unwrapSingle != _unwrapSingle)
193+
|| (nuller != _nullProvider)
192194
|| (delegateDeser != _delegateDeserializer)
193195
|| (valueDeser != _valueDeserializer)
194196
|| (valueTypeDeser != _valueTypeDeserializer)
195197
) {
196198
return withResolved(delegateDeser, valueDeser, valueTypeDeser,
197-
findContentNullProvider(ctxt, property, valueDeser),
198-
unwrapSingle);
199+
nuller, unwrapSingle);
199200
}
200201
return this;
201202
}
@@ -276,6 +277,9 @@ public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt,
276277
try {
277278
Object value;
278279
if (t == JsonToken.VALUE_NULL) {
280+
if (_skipNullValues) {
281+
continue;
282+
}
279283
value = _nullProvider.getNullValue(ctxt);
280284
} else if (typeDeser == null) {
281285
value = valueDes.deserialize(p, ctxt);
@@ -339,6 +343,10 @@ protected final Collection<Object> handleNonArray(JsonParser p, DeserializationC
339343

340344
try {
341345
if (t == JsonToken.VALUE_NULL) {
346+
// 03-Feb-2017, tatu: Hmmh. I wonder... let's try skipping here, too
347+
if (_skipNullValues) {
348+
return result;
349+
}
342350
value = _nullProvider.getNullValue(ctxt);
343351
} else if (typeDeser == null) {
344352
value = valueDes.deserialize(p, ctxt);

src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.fasterxml.jackson.databind.deser.NullValueProvider;
88
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
99
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
10+
import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
1011
import com.fasterxml.jackson.databind.type.TypeFactory;
1112
import com.fasterxml.jackson.databind.util.AccessPattern;
1213
import com.fasterxml.jackson.databind.util.ClassUtil;
@@ -37,13 +38,22 @@ public abstract class ContainerDeserializerBase<T>
3738
* @since 2.9 (demoted from sub-classes where added in 2.7)
3839
*/
3940
protected final Boolean _unwrapSingle;
40-
41+
42+
/**
43+
* Marker flag set if the <code>_nullProvider</code> indicates that all null
44+
* content values should be skipped (instead of being possibly converted).
45+
*
46+
* @since 2.9
47+
*/
48+
protected final boolean _skipNullValues;
49+
4150
protected ContainerDeserializerBase(JavaType selfType,
4251
NullValueProvider nuller, Boolean unwrapSingle) {
4352
super(selfType);
4453
_containerType = selfType;
4554
_unwrapSingle = unwrapSingle;
4655
_nullProvider = nuller;
56+
_skipNullValues = NullsConstantProvider.isSkipper(nuller);
4757
}
4858

4959
protected ContainerDeserializerBase(JavaType selfType) {
@@ -66,6 +76,7 @@ protected ContainerDeserializerBase(ContainerDeserializerBase<?> base,
6676
_containerType = base._containerType;
6777
_nullProvider = nuller;
6878
_unwrapSingle = unwrapSingle;
79+
_skipNullValues = NullsConstantProvider.isSkipper(nuller);
6980
}
7081

7182
/*

src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,15 @@ public EnumMap<?,?> deserialize(JsonParser p, DeserializationContext ctxt)
167167
}
168168
// And then the value...
169169
JsonToken t = p.nextToken();
170-
/* note: MUST check for nulls separately: deserializers will
171-
* not handle them (and maybe fail or return bogus data)
172-
*/
170+
// note: MUST check for nulls separately: deserializers will
171+
// not handle them (and maybe fail or return bogus data)
173172
Object value;
174173

175174
try {
176175
if (t == JsonToken.VALUE_NULL) {
176+
if (_skipNullValues) {
177+
continue;
178+
}
177179
value = _nullProvider.getNullValue(ctxt);
178180
} else if (typeDeser == null) {
179181
value = valueDes.deserialize(p, ctxt);
@@ -190,7 +192,7 @@ public EnumMap<?,?> deserialize(JsonParser p, DeserializationContext ctxt)
190192

191193
@Override
192194
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
193-
throws IOException, JsonProcessingException
195+
throws IOException
194196
{
195197
// In future could check current token... for now this should be enough:
196198
return typeDeserializer.deserializeTypedFromObject(jp, ctxt);

src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ public class MapDeserializer
6666

6767
protected final ValueInstantiator _valueInstantiator;
6868

69-
protected final boolean _hasDefaultCreator;
70-
7169
/**
7270
* Deserializer that is used iff delegate-based creator is
7371
* to be used for deserializing from JSON Object.
@@ -82,6 +80,8 @@ public class MapDeserializer
8280
*/
8381
protected PropertyBasedCreator _propertyBasedCreator;
8482

83+
protected final boolean _hasDefaultCreator;
84+
8585
// // Any properties to ignore if seen?
8686

8787
protected Set<String> _ignorableProperties;
@@ -458,6 +458,9 @@ protected final void _readAndBind(JsonParser p, DeserializationContext ctxt,
458458
// Note: must handle null explicitly here; value deserializers won't
459459
Object value;
460460
if (t == JsonToken.VALUE_NULL) {
461+
if (_skipNullValues) {
462+
continue;
463+
}
461464
value = _nullProvider.getNullValue(ctxt);
462465
} else if (typeDeser == null) {
463466
value = valueDes.deserialize(p, ctxt);
@@ -517,6 +520,9 @@ protected final void _readAndBindStringKeyMap(JsonParser p, DeserializationConte
517520
// Note: must handle null explicitly here; value deserializers won't
518521
Object value;
519522
if (t == JsonToken.VALUE_NULL) {
523+
if (_skipNullValues) {
524+
continue;
525+
}
520526
value = _nullProvider.getNullValue(ctxt);
521527
} else if (typeDeser == null) {
522528
value = valueDes.deserialize(p, ctxt);
@@ -585,6 +591,9 @@ public Map<Object,Object> _deserializeUsingCreator(JsonParser p, Deserialization
585591

586592
try {
587593
if (t == JsonToken.VALUE_NULL) {
594+
if (_skipNullValues) {
595+
continue;
596+
}
588597
value = _nullProvider.getNullValue(ctxt);
589598
} else if (typeDeser == null) {
590599
value = valueDes.deserialize(p, ctxt);

src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ public Object[] deserialize(JsonParser p, DeserializationContext ctxt)
187187
Object value;
188188

189189
if (t == JsonToken.VALUE_NULL) {
190+
if (_skipNullValues) {
191+
continue;
192+
}
190193
value = _nullProvider.getNullValue(ctxt);
191194
} else if (typeDeser == null) {
192195
value = _elementDeserializer.deserialize(p, ctxt);
@@ -219,9 +222,8 @@ public Object[] deserializeWithType(JsonParser p, DeserializationContext ctxt,
219222
TypeDeserializer typeDeserializer)
220223
throws IOException
221224
{
222-
/* Should there be separate handling for base64 stuff?
223-
* for now this should be enough:
224-
*/
225+
// Should there be separate handling for base64 stuff?
226+
// for now this should be enough:
225227
return (Object[]) typeDeserializer.deserializeTypedFromArray(p, ctxt);
226228
}
227229

@@ -252,6 +254,9 @@ public Object[] deserialize(JsonParser p, DeserializationContext ctxt,
252254
Object value;
253255

254256
if (t == JsonToken.VALUE_NULL) {
257+
if (_skipNullValues) {
258+
continue;
259+
}
255260
value = _nullProvider.getNullValue(ctxt);
256261
} else if (typeDeser == null) {
257262
value = _elementDeserializer.deserialize(p, ctxt);
@@ -328,6 +333,10 @@ protected Object[] handleNonArray(JsonParser p, DeserializationContext ctxt)
328333
Object value;
329334

330335
if (t == JsonToken.VALUE_NULL) {
336+
// 03-Feb-2017, tatu: Should this be skipped or not?
337+
if (_skipNullValues) {
338+
return NO_OBJECTS;
339+
}
331340
value = _nullProvider.getNullValue(ctxt);
332341
} else if (_elementTypeDeserializer == null) {
333342
value = _elementDeserializer.deserialize(p, ctxt);

0 commit comments

Comments
 (0)