Skip to content

Commit ca3ef05

Browse files
committed
Start implementing "content nulls" handling for some of collections
1 parent 2e9df7d commit ca3ef05

12 files changed

Lines changed: 336 additions & 106 deletions

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

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import java.util.*;
77

88
import com.fasterxml.jackson.annotation.*;
9-
import com.fasterxml.jackson.annotation.JsonSetter.Nulls;
9+
1010
import com.fasterxml.jackson.core.*;
1111
import com.fasterxml.jackson.core.JsonParser.NumberType;
1212

@@ -920,46 +920,13 @@ protected SettableBeanProperty _resolveMergeAndNullSettings(DeserializationConte
920920
}
921921

922922
// And after this, see if we require non-standard null handling
923-
NullValueProvider nuller = _findNullProvider(ctxt, prop, propMetadata);
923+
NullValueProvider nuller = findValueNullProvider(ctxt, prop, propMetadata);
924924
if (nuller != null) {
925925
prop = prop.withNullProvider(nuller);
926926
}
927927
return prop;
928928
}
929929

930-
// @since 2.9
931-
protected NullValueProvider _findNullProvider(DeserializationContext ctxt,
932-
SettableBeanProperty prop, PropertyMetadata propMetadata)
933-
throws JsonMappingException
934-
{
935-
final Nulls nulls = propMetadata.getValueNulls();
936-
if (nulls != null) {
937-
switch (nulls) {
938-
case FAIL:
939-
return new NullsFailProvider(prop.getFullName(), prop.getType());
940-
case AS_EMPTY:
941-
// Let's first do some sanity checking...
942-
{
943-
JsonDeserializer<?> deser = prop.getValueDeserializer();
944-
// NOTE: although we could use `ValueInstantiator.Gettable` in general,
945-
// let's not since that would prevent being able to use custom impls:
946-
if (deser instanceof BeanDeserializerBase) {
947-
ValueInstantiator vi = ((BeanDeserializerBase) deser).getValueInstantiator();
948-
if (!vi.canCreateUsingDefault()) {
949-
final JavaType type = prop.getType();
950-
ctxt.reportBadDefinition(type,
951-
String.format("Can not create empty instance of %s, no default Creator", type));
952-
}
953-
}
954-
return new NullsAsEmptyProvider(deser);
955-
}
956-
case SKIP: // can't do here
957-
default: // SET/DEFAULT, nothing to do; S
958-
}
959-
}
960-
return null;
961-
}
962-
963930
/*
964931
/**********************************************************
965932
/* Public accessors

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
*/
1515
public interface NullValueProvider
1616
{
17+
/**
18+
* Marker object used in some context to mark return value that is to indicate
19+
* that `null` should be skipped (instead of being replaced).
20+
*/
21+
public final static Object SKIP_MARKER = new Object();
22+
1723
/**
1824
* Method called to possibly convert incoming `null` token (read via
1925
* underlying streaming input source) into other value of type accessor
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.fasterxml.jackson.databind.deser.impl;
2+
3+
import com.fasterxml.jackson.databind.*;
4+
import com.fasterxml.jackson.databind.deser.NullValueProvider;
5+
import com.fasterxml.jackson.databind.exc.InvalidNullException;
6+
7+
/**
8+
* Simple {@link NullValueProvider} that will always throw a
9+
* {@link InvalidNullException} when a null is encountered.
10+
*/
11+
public class NullsConstantProvider implements NullValueProvider
12+
{
13+
private final static NullsConstantProvider SKIPPER = new NullsConstantProvider(NullValueProvider.SKIP_MARKER);
14+
15+
protected final Object _nullValue;
16+
17+
public NullsConstantProvider(Object nvl) {
18+
_nullValue = nvl;
19+
}
20+
21+
/**
22+
* Static accessor for a stateless instance that always returns
23+
* {@link NullValueProvider#SKIP_MARKER} marker instance, used to indicate
24+
* that null value is to be skipped instead of replacing it.
25+
*/
26+
public static NullsConstantProvider skipper() {
27+
return SKIPPER;
28+
}
29+
30+
@Override
31+
public Object getNullValue(DeserializationContext ctxt) {
32+
return _nullValue;
33+
}
34+
}

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import com.fasterxml.jackson.core.JsonParser;
88
import com.fasterxml.jackson.databind.*;
9+
import com.fasterxml.jackson.databind.deser.NullValueProvider;
910
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
1011
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
1112

@@ -38,10 +39,11 @@ public ArrayBlockingQueueDeserializer(JavaType containerType,
3839
protected ArrayBlockingQueueDeserializer(JavaType containerType,
3940
JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
4041
ValueInstantiator valueInstantiator,
41-
JsonDeserializer<Object> delegateDeser, Boolean unwrapSingle)
42+
JsonDeserializer<Object> delegateDeser,
43+
NullValueProvider nuller, Boolean unwrapSingle)
4244
{
43-
super(containerType, valueDeser, valueTypeDeser, valueInstantiator,
44-
delegateDeser, unwrapSingle);
45+
super(containerType, valueDeser, valueTypeDeser, valueInstantiator, delegateDeser,
46+
nuller, unwrapSingle);
4547
}
4648

4749
/**
@@ -58,11 +60,13 @@ protected ArrayBlockingQueueDeserializer(ArrayBlockingQueueDeserializer src) {
5860
@Override
5961
@SuppressWarnings("unchecked")
6062
protected ArrayBlockingQueueDeserializer withResolved(JsonDeserializer<?> dd,
61-
JsonDeserializer<?> vd, TypeDeserializer vtd, Boolean unwrapSingle)
63+
JsonDeserializer<?> vd, TypeDeserializer vtd,
64+
NullValueProvider nuller, Boolean unwrapSingle)
6265
{
6366
return new ArrayBlockingQueueDeserializer(_containerType,
6467
(JsonDeserializer<Object>) vd, vtd,
65-
_valueInstantiator, (JsonDeserializer<Object>) dd, unwrapSingle);
68+
_valueInstantiator, (JsonDeserializer<Object>) dd,
69+
nuller, unwrapSingle);
6670
}
6771

6872
/*

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

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
import java.util.*;
55

66
import com.fasterxml.jackson.annotation.JsonFormat;
7+
78
import com.fasterxml.jackson.core.*;
9+
810
import com.fasterxml.jackson.databind.*;
911
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
10-
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
11-
import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference;
12-
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
12+
import com.fasterxml.jackson.databind.deser.*;
1313
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
1414
import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase;
1515
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
@@ -54,6 +54,13 @@ 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+
5764
/**
5865
* Specific override for this instance (from proper, or global per-type overrides)
5966
* to indicate whether single value may be taken to mean an unwrapped one-element array
@@ -79,24 +86,26 @@ public CollectionDeserializer(JavaType collectionType,
7986
JsonDeserializer<Object> valueDeser,
8087
TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator)
8188
{
82-
this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null);
89+
this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null, null, null);
8390
}
8491

8592
/**
8693
* Constructor used when creating contextualized instances.
94+
*
95+
* @since 2.9
8796
*/
8897
protected CollectionDeserializer(JavaType collectionType,
8998
JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
90-
ValueInstantiator valueInstantiator,
91-
JsonDeserializer<Object> delegateDeser,
92-
Boolean unwrapSingle)
99+
ValueInstantiator valueInstantiator, JsonDeserializer<Object> delegateDeser,
100+
NullValueProvider nuller, Boolean unwrapSingle)
93101
{
94102
super(collectionType);
95103
_valueDeserializer = valueDeser;
96104
_valueTypeDeserializer = valueTypeDeser;
97105
_valueInstantiator = valueInstantiator;
98106
_delegateDeserializer = delegateDeser;
99107
_unwrapSingle = unwrapSingle;
108+
_nullProvider = nuller;
100109
}
101110

102111
/**
@@ -111,21 +120,23 @@ protected CollectionDeserializer(CollectionDeserializer src)
111120
_valueInstantiator = src._valueInstantiator;
112121
_delegateDeserializer = src._delegateDeserializer;
113122
_unwrapSingle = src._unwrapSingle;
123+
_nullProvider = src._nullProvider;
114124
}
115125

116126
/**
117127
* Fluent-factory method call to construct contextual instance.
118128
*
119-
* @since 2.7
129+
* @since 2.9
120130
*/
121131
@SuppressWarnings("unchecked")
122132
protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
123133
JsonDeserializer<?> vd, TypeDeserializer vtd,
124-
Boolean unwrapSingle)
134+
NullValueProvider nuller, Boolean unwrapSingle)
125135
{
126136
return new CollectionDeserializer(_containerType,
127137
(JsonDeserializer<Object>) vd, vtd,
128-
_valueInstantiator, (JsonDeserializer<Object>) dd, unwrapSingle);
138+
_valueInstantiator, (JsonDeserializer<Object>) dd,
139+
nuller, unwrapSingle);
129140
}
130141

131142
/**
@@ -135,7 +146,7 @@ protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
135146
protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
136147
JsonDeserializer<?> vd, TypeDeserializer vtd)
137148
{
138-
return withResolved(dd, vd, vtd, _unwrapSingle);
149+
return withResolved(dd, vd, vtd, vd, _unwrapSingle);
139150
}
140151

141152
// Important: do NOT cache if polymorphic values
@@ -212,7 +223,9 @@ public CollectionDeserializer createContextual(DeserializationContext ctxt,
212223
|| (valueDeser != _valueDeserializer)
213224
|| (valueTypeDeser != _valueTypeDeserializer)
214225
) {
215-
return withResolved(delegateDeser, valueDeser, valueTypeDeser, unwrapSingle);
226+
return withResolved(delegateDeser, valueDeser, valueTypeDeser,
227+
findContentNullProvider(ctxt, property, valueDeser),
228+
unwrapSingle);
216229
}
217230
return this;
218231
}
@@ -293,7 +306,7 @@ public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt,
293306
try {
294307
Object value;
295308
if (t == JsonToken.VALUE_NULL) {
296-
value = valueDes.getNullValue(ctxt);
309+
value = _nullProvider.getNullValue(ctxt);
297310
} else if (typeDeser == null) {
298311
value = valueDes.deserialize(p, ctxt);
299312
} else {
@@ -356,7 +369,7 @@ protected final Collection<Object> handleNonArray(JsonParser p, DeserializationC
356369

357370
try {
358371
if (t == JsonToken.VALUE_NULL) {
359-
value = valueDes.getNullValue(ctxt);
372+
value = _nullProvider.getNullValue(ctxt);
360373
} else if (typeDeser == null) {
361374
value = valueDes.deserialize(p, ctxt);
362375
} else {

0 commit comments

Comments
 (0)