Skip to content

Commit ddef782

Browse files
committed
Merge pull request #1053 from hgwood/array_obj_fix
Fixed array delegate creator not being fully functional
2 parents a49ab9a + 8497e4d commit ddef782

File tree

4 files changed

+100
-19
lines changed

4 files changed

+100
-19
lines changed

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

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ public abstract class BeanDeserializerBase
7777
* to be used for deserializing from JSON Object.
7878
*/
7979
protected JsonDeserializer<Object> _delegateDeserializer;
80+
81+
/**
82+
* Deserializer that is used iff array-delegate-based creator
83+
* is to be used for deserializing from JSON Object.
84+
*/
85+
protected JsonDeserializer<Object> _arrayDelegateDeserializer;
8086

8187
/**
8288
* If the bean needs to be instantiated using constructor
@@ -526,22 +532,20 @@ public void resolve(DeserializationContext ctxt)
526532
+": value instantiator ("+_valueInstantiator.getClass().getName()
527533
+") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'");
528534
}
529-
AnnotatedWithParams delegateCreator = _valueInstantiator.getDelegateCreator();
530-
// Need to create a temporary property to allow contextual deserializers:
531-
BeanProperty.Std property = new BeanProperty.Std(TEMP_PROPERTY_NAME,
532-
delegateType, null, _classAnnotations, delegateCreator,
533-
PropertyMetadata.STD_OPTIONAL);
534-
535-
TypeDeserializer td = delegateType.getTypeHandler();
536-
if (td == null) {
537-
td = ctxt.getConfig().findTypeDeserializer(delegateType);
538-
}
539-
JsonDeserializer<Object> dd = findDeserializer(ctxt, delegateType, property);
540-
if (td != null) {
541-
td = td.forProperty(property);
542-
dd = new TypeWrappedDeserializer(td, dd);
535+
_delegateDeserializer = _findDelegateDeserializer(ctxt, delegateType,
536+
_valueInstantiator.getDelegateCreator());
537+
}
538+
539+
// and array-delegate-based constructor:
540+
if (_valueInstantiator.canCreateUsingArrayDelegate()) {
541+
JavaType delegateType = _valueInstantiator.getArrayDelegateType(ctxt.getConfig());
542+
if (delegateType == null) {
543+
throw new IllegalArgumentException("Invalid array-delegate-creator definition for "+_beanType
544+
+": value instantiator ("+_valueInstantiator.getClass().getName()
545+
+") returned true for 'canCreateUsingArrayDelegate()', but null for 'getArrayDelegateType()'");
543546
}
544-
_delegateDeserializer = dd;
547+
_arrayDelegateDeserializer = _findDelegateDeserializer(ctxt, delegateType,
548+
_valueInstantiator.getArrayDelegateCreator());
545549
}
546550

547551
// And now that we know CreatorProperty instances are also resolved can finally create the creator:
@@ -564,6 +568,26 @@ public void resolve(DeserializationContext ctxt)
564568
_vanillaProcessing = _vanillaProcessing && !_nonStandardCreation;
565569
}
566570

571+
private JsonDeserializer<Object> _findDelegateDeserializer(DeserializationContext ctxt, JavaType delegateType,
572+
AnnotatedWithParams delegateCreator) throws JsonMappingException {
573+
// Need to create a temporary property to allow contextual deserializers:
574+
BeanProperty.Std property = new BeanProperty.Std(TEMP_PROPERTY_NAME,
575+
delegateType, null, _classAnnotations, delegateCreator,
576+
PropertyMetadata.STD_OPTIONAL);
577+
578+
TypeDeserializer td = delegateType.getTypeHandler();
579+
if (td == null) {
580+
td = ctxt.getConfig().findTypeDeserializer(delegateType);
581+
}
582+
JsonDeserializer<Object> dd = findDeserializer(ctxt, delegateType, property);
583+
if (td != null) {
584+
td = td.forProperty(property);
585+
return new TypeWrappedDeserializer(td, dd);
586+
}
587+
return dd;
588+
}
589+
590+
567591
/**
568592
* Helper method that can be used to see if specified property is annotated
569593
* to indicate use of a converter for property value (in case of container types,
@@ -1223,6 +1247,18 @@ public Object deserializeFromBoolean(JsonParser p, DeserializationContext ctxt)
12231247

12241248
public Object deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
12251249
{
1250+
if (_arrayDelegateDeserializer != null) {
1251+
try {
1252+
Object bean = _valueInstantiator.createUsingArrayDelegate(ctxt, _arrayDelegateDeserializer.deserialize(p, ctxt));
1253+
if (_injectables != null) {
1254+
injectValues(ctxt, bean);
1255+
}
1256+
return bean;
1257+
} catch (Exception e) {
1258+
wrapInstantiationProblem(e, ctxt);
1259+
}
1260+
}
1261+
// fallback to non-array delegate
12261262
if (_delegateDeserializer != null) {
12271263
try {
12281264
Object bean = _valueInstantiator.createUsingArrayDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ public boolean canInstantiate() {
9898
*/
9999
public boolean canCreateUsingDelegate() { return false; }
100100

101+
/**
102+
* Method that can be called to check whether a array-delegate-based creator
103+
* (single-arg constructor or factory method)
104+
* is available for this instantiator
105+
*/
106+
public boolean canCreateUsingArrayDelegate() { return false; }
107+
101108
/**
102109
* Method that can be called to check whether a property-based creator
103110
* (argument-taking constructor or factory method)
@@ -127,6 +134,15 @@ public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig confi
127134
* pass that to instantiator.
128135
*/
129136
public JavaType getDelegateType(DeserializationConfig config) { return null; }
137+
138+
/**
139+
* Method that can be used to determine what is the type of array delegate
140+
* type to use, if any; if no delegates are used, will return null. If
141+
* non-null type is returned, deserializer will bind JSON into specified
142+
* type (using standard deserializer for that type), and pass that to
143+
* instantiator.
144+
*/
145+
public JavaType getArrayDelegateType(DeserializationConfig config) { return null; }
130146

131147
/*
132148
/**********************************************************
@@ -239,6 +255,16 @@ public Object createFromBoolean(DeserializationContext ctxt, boolean value) thro
239255
*/
240256
public AnnotatedWithParams getDelegateCreator() { return null; }
241257

258+
/**
259+
* Method that can be called to try to access member (constructor,
260+
* static factory method) that is used as the "array delegate creator".
261+
* Note that implementations not required to return actual object
262+
* they use (or, they may use some other instantiation) method.
263+
* That is, even if {@link #canCreateUsingArrayDelegate()} returns true,
264+
* this method may return null .
265+
*/
266+
public AnnotatedWithParams getArrayDelegateCreator() { return null; }
267+
242268
/**
243269
* Method that can be called to try to access member (constructor,
244270
* static factory method) that is used as the "non-default creator"

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ public boolean canCreateUsingDefault() {
201201
public boolean canCreateUsingDelegate() {
202202
return _delegateType != null;
203203
}
204+
205+
@Override
206+
public boolean canCreateUsingArrayDelegate() {
207+
return _arrayDelegateType != null;
208+
}
204209

205210
@Override
206211
public boolean canCreateFromObjectWith() {
@@ -212,6 +217,11 @@ public JavaType getDelegateType(DeserializationConfig config) {
212217
return _delegateType;
213218
}
214219

220+
@Override
221+
public JavaType getArrayDelegateType(DeserializationConfig config) {
222+
return _arrayDelegateType;
223+
}
224+
215225
@Override
216226
public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) {
217227
return _constructorArguments;
@@ -359,6 +369,11 @@ public AnnotatedWithParams getDelegateCreator() {
359369
return _delegateCreator;
360370
}
361371

372+
@Override
373+
public AnnotatedWithParams getArrayDelegateCreator() {
374+
return _arrayDelegateCreator;
375+
}
376+
362377
@Override
363378
public AnnotatedWithParams getDefaultCreator() {
364379
return _defaultCreator;

src/test/java/com/fasterxml/jackson/databind/deser/TestObjectOrArrayDeserialization.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,22 @@
88

99
public class TestObjectOrArrayDeserialization extends BaseMapTest
1010
{
11+
public static class SomeObject {
12+
public String someField;
13+
}
1114

1215
public static class ArrayOrObject {
13-
private final List<Object> objects;
14-
private final Object object;
16+
private final List<SomeObject> objects;
17+
private final SomeObject object;
1518

1619
@JsonCreator
17-
public ArrayOrObject(List<Object> objects) {
20+
public ArrayOrObject(List<SomeObject> objects) {
1821
this.objects = objects;
1922
this.object = null;
2023
}
2124

2225
@JsonCreator
23-
public ArrayOrObject(Object object) {
26+
public ArrayOrObject(SomeObject object) {
2427
this.objects = null;
2528
this.object = object;
2629
}
@@ -45,4 +48,5 @@ public void testNotEmptyArrayCase() throws Exception {
4548
assertEquals("expected objects field to have size 2", 2, arrayOrObject.objects.size());
4649
assertNull("expected object field to be null", arrayOrObject.object);
4750
}
51+
4852
}

0 commit comments

Comments
 (0)