Skip to content

Commit 3fc3177

Browse files
committed
more work on null handling wrt containers
1 parent ca3ef05 commit 3fc3177

File tree

6 files changed

+140
-91
lines changed

6 files changed

+140
-91
lines changed

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

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,6 @@ public class CollectionDeserializer
5454
*/
5555
protected final JsonDeserializer<Object> _delegateDeserializer;
5656

57-
/**
58-
* Handler we need for dealing with nulls
59-
*
60-
* @since 2.9
61-
*/
62-
protected final NullValueProvider _nullProvider;
63-
64-
/**
65-
* Specific override for this instance (from proper, or global per-type overrides)
66-
* to indicate whether single value may be taken to mean an unwrapped one-element array
67-
* or not. If null, left to global defaults.
68-
*
69-
* @since 2.7
70-
*/
71-
protected final Boolean _unwrapSingle;
72-
7357
// NOTE: no PropertyBasedCreator, as JSON Arrays have no properties
7458

7559
/*
@@ -99,13 +83,11 @@ protected CollectionDeserializer(JavaType collectionType,
9983
ValueInstantiator valueInstantiator, JsonDeserializer<Object> delegateDeser,
10084
NullValueProvider nuller, Boolean unwrapSingle)
10185
{
102-
super(collectionType);
86+
super(collectionType, nuller, unwrapSingle);
10387
_valueDeserializer = valueDeser;
10488
_valueTypeDeserializer = valueTypeDeser;
10589
_valueInstantiator = valueInstantiator;
10690
_delegateDeserializer = delegateDeser;
107-
_unwrapSingle = unwrapSingle;
108-
_nullProvider = nuller;
10991
}
11092

11193
/**
@@ -114,13 +96,11 @@ protected CollectionDeserializer(JavaType collectionType,
11496
*/
11597
protected CollectionDeserializer(CollectionDeserializer src)
11698
{
117-
super(src._containerType);
99+
super(src);
118100
_valueDeserializer = src._valueDeserializer;
119101
_valueTypeDeserializer = src._valueTypeDeserializer;
120102
_valueInstantiator = src._valueInstantiator;
121103
_delegateDeserializer = src._delegateDeserializer;
122-
_unwrapSingle = src._unwrapSingle;
123-
_nullProvider = src._nullProvider;
124104
}
125105

126106
/**

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

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.lang.reflect.InvocationTargetException;
55

66
import com.fasterxml.jackson.databind.*;
7+
import com.fasterxml.jackson.databind.deser.NullValueProvider;
78
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
89
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
910
import com.fasterxml.jackson.databind.type.TypeFactory;
@@ -20,9 +21,51 @@ public abstract class ContainerDeserializerBase<T>
2021
{
2122
protected final JavaType _containerType;
2223

23-
protected ContainerDeserializerBase(JavaType selfType) {
24+
/**
25+
* Handler we need for dealing with nulls.
26+
*
27+
* @since 2.9
28+
*/
29+
protected final NullValueProvider _nullProvider;
30+
31+
/**
32+
* Specific override for this instance (from proper, or global per-type overrides)
33+
* to indicate whether single value may be taken to mean an unwrapped one-element array
34+
* or not. If null, left to global defaults.
35+
*
36+
* @since 2.9 (demoted from sub-classes where added in 2.7)
37+
*/
38+
protected final Boolean _unwrapSingle;
39+
40+
protected ContainerDeserializerBase(JavaType selfType,
41+
NullValueProvider nuller, Boolean unwrapSingle) {
2442
super(selfType);
2543
_containerType = selfType;
44+
_unwrapSingle = unwrapSingle;
45+
_nullProvider = nuller;
46+
}
47+
48+
/**
49+
* @since 2.9
50+
*/
51+
protected ContainerDeserializerBase(ContainerDeserializerBase<?> base) {
52+
this(base, base._nullProvider, base._unwrapSingle);
53+
}
54+
55+
/**
56+
* @since 2.9
57+
*/
58+
protected ContainerDeserializerBase(ContainerDeserializerBase<?> base,
59+
NullValueProvider nuller, Boolean unwrapSingle) {
60+
super(base._containerType);
61+
_containerType = base._containerType;
62+
_nullProvider = nuller;
63+
_unwrapSingle = unwrapSingle;
64+
}
65+
66+
@Deprecated // since 2.9
67+
protected ContainerDeserializerBase(JavaType selfType) {
68+
this(selfType, null, null);
2669
}
2770

2871
/*

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

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.fasterxml.jackson.databind.*;
1111
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
1212
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
13+
import com.fasterxml.jackson.databind.deser.NullValueProvider;
1314
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
1415
import com.fasterxml.jackson.databind.util.ObjectBuffer;
1516

@@ -50,15 +51,6 @@ public class ObjectArrayDeserializer
5051
*/
5152
protected final TypeDeserializer _elementTypeDeserializer;
5253

53-
/**
54-
* Specific override for this instance (from proper, or global per-type overrides)
55-
* to indicate whether single value may be taken to mean an unwrapped one-element array
56-
* or not. If null, left to global defaults.
57-
*
58-
* @since 2.7
59-
*/
60-
protected final Boolean _unwrapSingle;
61-
6254
/*
6355
/**********************************************************
6456
/* Life-cycle
@@ -68,78 +60,80 @@ public class ObjectArrayDeserializer
6860
public ObjectArrayDeserializer(JavaType arrayType,
6961
JsonDeserializer<Object> elemDeser, TypeDeserializer elemTypeDeser)
7062
{
71-
super(arrayType);
63+
super(arrayType, null, null);
7264
_elementClass = arrayType.getContentType().getRawClass();
7365
_untyped = (_elementClass == Object.class);
7466
_elementDeserializer = elemDeser;
7567
_elementTypeDeserializer = elemTypeDeser;
76-
_unwrapSingle = null;
7768
}
7869

7970
protected ObjectArrayDeserializer(ObjectArrayDeserializer base,
8071
JsonDeserializer<Object> elemDeser, TypeDeserializer elemTypeDeser,
81-
Boolean unwrapSingle)
72+
NullValueProvider nuller, Boolean unwrapSingle)
8273
{
83-
super(base._containerType);
74+
super(base, nuller, unwrapSingle);
8475
_elementClass = base._elementClass;
8576
_untyped = base._untyped;
8677

8778
_elementDeserializer = elemDeser;
8879
_elementTypeDeserializer = elemTypeDeser;
89-
_unwrapSingle = unwrapSingle;
9080
}
91-
81+
9282
/**
9383
* Overridable fluent-factory method used to create contextual instances
9484
*/
9585
public ObjectArrayDeserializer withDeserializer(TypeDeserializer elemTypeDeser,
9686
JsonDeserializer<?> elemDeser)
9787
{
98-
return withResolved(elemTypeDeser, elemDeser, _unwrapSingle);
88+
return withResolved(elemTypeDeser, elemDeser,
89+
_nullProvider, _unwrapSingle);
9990
}
10091

10192
/**
10293
* @since 2.7
10394
*/
10495
@SuppressWarnings("unchecked")
10596
public ObjectArrayDeserializer withResolved(TypeDeserializer elemTypeDeser,
106-
JsonDeserializer<?> elemDeser, Boolean unwrapSingle)
97+
JsonDeserializer<?> elemDeser, NullValueProvider nuller, Boolean unwrapSingle)
10798
{
108-
if ((unwrapSingle == _unwrapSingle)
99+
if ((unwrapSingle == _unwrapSingle) && (nuller == _nullProvider)
109100
&& (elemDeser == _elementDeserializer)
110101
&& (elemTypeDeser == _elementTypeDeserializer)) {
111102
return this;
112103
}
113104
return new ObjectArrayDeserializer(this,
114-
(JsonDeserializer<Object>) elemDeser, elemTypeDeser, unwrapSingle);
105+
(JsonDeserializer<Object>) elemDeser, elemTypeDeser,
106+
nuller, unwrapSingle);
107+
}
108+
109+
@Override // since 2.5
110+
public boolean isCachable() {
111+
// Important: do NOT cache if polymorphic values, or if there are annotation-based
112+
// custom deserializers
113+
return (_elementDeserializer == null) && (_elementTypeDeserializer == null);
115114
}
116115

117116
@Override
118117
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
119118
BeanProperty property) throws JsonMappingException
120119
{
121-
JsonDeserializer<?> deser = _elementDeserializer;
120+
JsonDeserializer<?> valueDeser = _elementDeserializer;
122121
Boolean unwrapSingle = findFormatFeature(ctxt, property, _containerType.getRawClass(),
123122
JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
124123
// May have a content converter
125-
deser = findConvertingContentDeserializer(ctxt, property, deser);
124+
valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
126125
final JavaType vt = _containerType.getContentType();
127-
if (deser == null) {
128-
deser = ctxt.findContextualValueDeserializer(vt, property);
126+
if (valueDeser == null) {
127+
valueDeser = ctxt.findContextualValueDeserializer(vt, property);
129128
} else { // if directly assigned, probably not yet contextual, so:
130-
deser = ctxt.handleSecondaryContextualization(deser, property, vt);
129+
valueDeser = ctxt.handleSecondaryContextualization(valueDeser, property, vt);
131130
}
132131
TypeDeserializer elemTypeDeser = _elementTypeDeserializer;
133132
if (elemTypeDeser != null) {
134133
elemTypeDeser = elemTypeDeser.forProperty(property);
135134
}
136-
return withResolved(elemTypeDeser, deser, unwrapSingle);
137-
}
138-
139-
@Override // since 2.5
140-
public boolean isCachable() {
141-
// Important: do NOT cache if polymorphic values, or ones with custom deserializer
142-
return (_elementDeserializer == null) && (_elementTypeDeserializer == null);
135+
NullValueProvider nuller = findContentNullProvider(ctxt, property, valueDeser);
136+
return withResolved(elemTypeDeser, valueDeser, nuller, unwrapSingle);
143137
}
144138

145139
/*
@@ -186,7 +180,7 @@ public Object[] deserialize(JsonParser p, DeserializationContext ctxt)
186180
Object value;
187181

188182
if (t == JsonToken.VALUE_NULL) {
189-
value = _elementDeserializer.getNullValue(ctxt);
183+
value = _nullProvider.getNullValue(ctxt);
190184
} else if (typeDeser == null) {
191185
value = _elementDeserializer.deserialize(p, ctxt);
192186
} else {
@@ -251,7 +245,7 @@ public Object[] deserialize(JsonParser p, DeserializationContext ctxt,
251245
Object value;
252246

253247
if (t == JsonToken.VALUE_NULL) {
254-
value = _elementDeserializer.getNullValue(ctxt);
248+
value = _nullProvider.getNullValue(ctxt);
255249
} else if (typeDeser == null) {
256250
value = _elementDeserializer.deserialize(p, ctxt);
257251
} else {
@@ -327,7 +321,7 @@ protected Object[] handleNonArray(JsonParser p, DeserializationContext ctxt)
327321
Object value;
328322

329323
if (t == JsonToken.VALUE_NULL) {
330-
value = _elementDeserializer.getNullValue(ctxt);
324+
value = _nullProvider.getNullValue(ctxt);
331325
} else if (_elementTypeDeserializer == null) {
332326
value = _elementDeserializer.deserialize(p, ctxt);
333327
} else {

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

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.fasterxml.jackson.databind.*;
88
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
99
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
10+
import com.fasterxml.jackson.databind.deser.NullValueProvider;
1011
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
1112
import com.fasterxml.jackson.databind.util.ObjectBuffer;
1213

@@ -34,6 +35,13 @@ public final class StringArrayDeserializer
3435
*/
3536
protected JsonDeserializer<String> _elementDeserializer;
3637

38+
/**
39+
* Handler we need for dealing with nulls.
40+
*
41+
* @since 2.9
42+
*/
43+
protected final NullValueProvider _nullProvider;
44+
3745
/**
3846
* Specific override for this instance (from proper, or global per-type overrides)
3947
* to indicate whether single value may be taken to mean an unwrapped one-element array
@@ -44,13 +52,15 @@ public final class StringArrayDeserializer
4452
protected final Boolean _unwrapSingle;
4553

4654
public StringArrayDeserializer() {
47-
this(null, null);
55+
this(null, null, null);
4856
}
4957

5058
@SuppressWarnings("unchecked")
51-
protected StringArrayDeserializer(JsonDeserializer<?> deser, Boolean unwrapSingle) {
59+
protected StringArrayDeserializer(JsonDeserializer<?> deser,
60+
NullValueProvider nuller, Boolean unwrapSingle) {
5261
super(String[].class);
5362
_elementDeserializer = (JsonDeserializer<String>) deser;
63+
_nullProvider = nuller;
5464
_unwrapSingle = unwrapSingle;
5565
}
5666

@@ -69,7 +79,8 @@ public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingExcep
6979
* of String values, or if we have to use separate value deserializer.
7080
*/
7181
@Override
72-
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException
82+
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
83+
throws JsonMappingException
7384
{
7485
JsonDeserializer<?> deser = _elementDeserializer;
7586
// May have a content converter
@@ -83,14 +94,17 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanPro
8394
// One more thing: allow unwrapping?
8495
Boolean unwrapSingle = findFormatFeature(ctxt, property, String[].class,
8596
JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
97+
NullValueProvider nuller = findContentNullProvider(ctxt, property, deser);
8698
// Ok ok: if all we got is the default String deserializer, can just forget about it
8799
if ((deser != null) && isDefaultDeserializer(deser)) {
88100
deser = null;
89101
}
90-
if ((_elementDeserializer == deser) && (_unwrapSingle == unwrapSingle)) {
102+
if ((_elementDeserializer == deser)
103+
&& (_unwrapSingle == unwrapSingle)
104+
&& (_nullProvider == nuller)) {
91105
return this;
92106
}
93-
return new StringArrayDeserializer(deser, unwrapSingle);
107+
return new StringArrayDeserializer(deser, nuller, unwrapSingle);
94108
}
95109

96110
@Override
@@ -117,7 +131,9 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt) throws IO
117131
if (t == JsonToken.END_ARRAY) {
118132
break;
119133
}
120-
if (t != JsonToken.VALUE_NULL) {
134+
if (t == JsonToken.VALUE_NULL) {
135+
value = (String) _nullProvider.getNullValue(ctxt);
136+
} else {
121137
value = _parseString(p, ctxt);
122138
}
123139
}
@@ -169,7 +185,11 @@ protected final String[] _deserializeCustom(JsonParser p, DeserializationContext
169185
break;
170186
}
171187
// Ok: no need to convert Strings, but must recognize nulls
172-
value = (t == JsonToken.VALUE_NULL) ? deser.getNullValue(ctxt) : deser.deserialize(p, ctxt);
188+
if (t == JsonToken.VALUE_NULL) {
189+
value = (String) _nullProvider.getNullValue(ctxt);
190+
} else {
191+
value = deser.deserialize(p, ctxt);
192+
}
173193
} else {
174194
value = deser.deserialize(p, ctxt);
175195
}
@@ -225,7 +245,9 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt,
225245
if (t == JsonToken.END_ARRAY) {
226246
break;
227247
}
228-
if (t != JsonToken.VALUE_NULL) {
248+
if (t == JsonToken.VALUE_NULL) {
249+
value = (String) _nullProvider.getNullValue(ctxt);
250+
} else {
229251
value = _parseString(p, ctxt);
230252
}
231253
}
@@ -242,15 +264,18 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt,
242264
ctxt.returnObjectBuffer(buffer);
243265
return result;
244266
}
245-
267+
246268
private final String[] handleNonArray(JsonParser p, DeserializationContext ctxt) throws IOException
247269
{
248270
// implicit arrays from single values?
249271
boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
250272
((_unwrapSingle == null) &&
251273
ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
252274
if (canWrap) {
253-
return new String[] { p.hasToken(JsonToken.VALUE_NULL) ? null : _parseString(p, ctxt) };
275+
String value = p.hasToken(JsonToken.VALUE_NULL)
276+
? (String) _nullProvider.getNullValue(ctxt)
277+
: _parseString(p, ctxt);
278+
return new String[] { value };
254279
}
255280
if (p.hasToken(JsonToken.VALUE_STRING)
256281
&& ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {

0 commit comments

Comments
 (0)