Skip to content

Commit 31477e4

Browse files
committed
Merge pull request #1010 from hgwood/array_obj
Support for array delegate creator
2 parents e9b3277 + 9dbee04 commit 31477e4

File tree

5 files changed

+161
-49
lines changed

5 files changed

+161
-49
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1225,7 +1225,7 @@ public Object deserializeFromArray(JsonParser p, DeserializationContext ctxt) th
12251225
{
12261226
if (_delegateDeserializer != null) {
12271227
try {
1228-
Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
1228+
Object bean = _valueInstantiator.createUsingArrayDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
12291229
if (_injectables != null) {
12301230
injectValues(ctxt, bean);
12311231
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,15 @@ public Object createUsingDelegate(DeserializationContext ctxt, Object delegate)
171171
getValueTypeDesc());
172172
}
173173

174+
/**
175+
* Method to called to create value instance from JSON Array using
176+
* an intermediate "delegate" value to pass to createor method
177+
*/
178+
public Object createUsingArrayDelegate(DeserializationContext ctxt, Object delegate) throws IOException {
179+
throw ctxt.mappingException("Can not instantiate value of type %s using delegate",
180+
getValueTypeDesc());
181+
}
182+
174183
/*
175184
/**********************************************************
176185
/* Instantiation methods for JSON scalar types

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

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class CreatorCollector
2828
protected final static int C_BOOLEAN = 5;
2929
protected final static int C_DELEGATE = 6;
3030
protected final static int C_PROPS = 7;
31+
protected final static int C_ARRAY_DELEGATE = 8;
3132

3233
protected final static String[] TYPE_DESCS = new String[] {
3334
"default",
@@ -50,7 +51,7 @@ public class CreatorCollector
5051
*
5152
* @since 2.5
5253
*/
53-
protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[8];
54+
protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[9];
5455

5556
/**
5657
* Bitmask of creators that were explicitly marked as creators; false for auto-detected
@@ -65,6 +66,8 @@ public class CreatorCollector
6566
// when there are injectable values along with delegate:
6667
protected SettableBeanProperty[] _delegateArgs;
6768

69+
protected SettableBeanProperty[] _arrayDelegateArgs;
70+
6871
protected SettableBeanProperty[] _propertyBasedArgs;
6972

7073
protected AnnotatedParameter _incompleteParameter;
@@ -84,32 +87,13 @@ public CreatorCollector(BeanDescription beanDesc, MapperConfig<?> config)
8487

8588
public ValueInstantiator constructValueInstantiator(DeserializationConfig config)
8689
{
87-
JavaType delegateType;
88-
boolean maybeVanilla = !_hasNonDefaultCreator;
89-
90-
if (maybeVanilla || (_creators[C_DELEGATE] == null)) {
91-
delegateType = null;
92-
} else {
93-
// need to find type...
94-
int ix = 0;
95-
if (_delegateArgs != null) {
96-
for (int i = 0, len = _delegateArgs.length; i < len; ++i) {
97-
if (_delegateArgs[i] == null) { // marker for delegate itself
98-
ix = i;
99-
break;
100-
}
101-
}
102-
}
103-
delegateType = _creators[C_DELEGATE].getParameterType(ix);
104-
}
105-
90+
final JavaType delegateType = _computeDelegateType(_creators[C_DELEGATE], _delegateArgs);
91+
final JavaType arrayDelegateType = _computeDelegateType(_creators[C_ARRAY_DELEGATE], _arrayDelegateArgs);
10692
final JavaType type = _beanDesc.getType();
10793

10894
// Any non-standard creator will prevent; with one exception: int-valued constructor
10995
// that standard containers have can be ignored
110-
maybeVanilla &= !_hasNonDefaultCreator;
111-
112-
if (maybeVanilla) {
96+
if (!_hasNonDefaultCreator) {
11397
/* 10-May-2014, tatu: If we have nothing special, and we are dealing with one
11498
* of "well-known" types, can create a non-reflection-based instantiator.
11599
*/
@@ -129,6 +113,7 @@ public ValueInstantiator constructValueInstantiator(DeserializationConfig config
129113
inst.configureFromObjectSettings(_creators[C_DEFAULT],
130114
_creators[C_DELEGATE], delegateType, _delegateArgs,
131115
_creators[C_PROPS], _propertyBasedArgs);
116+
inst.configureFromArraySettings(_creators[C_ARRAY_DELEGATE], arrayDelegateType, _arrayDelegateArgs);
132117
inst.configureFromStringCreator(_creators[C_STRING]);
133118
inst.configureFromIntCreator(_creators[C_INT]);
134119
inst.configureFromLongCreator(_creators[C_LONG]);
@@ -176,8 +161,13 @@ public void addBooleanCreator(AnnotatedWithParams creator, boolean explicit) {
176161
public void addDelegatingCreator(AnnotatedWithParams creator, boolean explicit,
177162
SettableBeanProperty[] injectables)
178163
{
179-
verifyNonDup(creator, C_DELEGATE, explicit);
180-
_delegateArgs = injectables;
164+
if (creator.getParameterType(0).isCollectionLikeType()) {
165+
verifyNonDup(creator, C_ARRAY_DELEGATE, explicit);
166+
_arrayDelegateArgs = injectables;
167+
} else {
168+
verifyNonDup(creator, C_DELEGATE, explicit);
169+
_delegateArgs = injectables;
170+
}
181171
}
182172

183173
public void addPropertyCreator(AnnotatedWithParams creator, boolean explicit,
@@ -276,6 +266,25 @@ public boolean hasPropertyBasedCreator() {
276266
/**********************************************************
277267
*/
278268

269+
private JavaType _computeDelegateType(AnnotatedWithParams creator, SettableBeanProperty[] delegateArgs)
270+
{
271+
if (!_hasNonDefaultCreator || (creator == null)) {
272+
return null;
273+
} else {
274+
// need to find type...
275+
int ix = 0;
276+
if (delegateArgs != null) {
277+
for (int i = 0, len = delegateArgs.length; i < len; ++i) {
278+
if (delegateArgs[i] == null) { // marker for delegate itself
279+
ix = i;
280+
break;
281+
}
282+
}
283+
}
284+
return creator.getParameterType(ix);
285+
}
286+
}
287+
279288
private <T extends AnnotatedMember> T _fixAccess(T member)
280289
{
281290
if (member != null && _canFixAccess) {

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

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ public class StdValueInstantiator
4545
protected JavaType _delegateType;
4646
protected AnnotatedWithParams _delegateCreator;
4747
protected SettableBeanProperty[] _delegateArguments;
48+
49+
// // // Array delegate construction
50+
51+
protected JavaType _arrayDelegateType;
52+
protected AnnotatedWithParams _arrayDelegateCreator;
53+
protected SettableBeanProperty[] _arrayDelegateArguments;
4854

4955
// // // Scalar construction
5056

@@ -87,6 +93,10 @@ protected StdValueInstantiator(StdValueInstantiator src)
8793
_delegateType = src._delegateType;
8894
_delegateCreator = src._delegateCreator;
8995
_delegateArguments = src._delegateArguments;
96+
97+
_arrayDelegateType = src._arrayDelegateType;
98+
_arrayDelegateCreator = src._arrayDelegateCreator;
99+
_arrayDelegateArguments = src._arrayDelegateArguments;
90100

91101
_fromStringCreator = src._fromStringCreator;
92102
_fromIntCreator = src._fromIntCreator;
@@ -112,6 +122,16 @@ public void configureFromObjectSettings(AnnotatedWithParams defaultCreator,
112122
_constructorArguments = constructorArgs;
113123
}
114124

125+
public void configureFromArraySettings(
126+
AnnotatedWithParams arrayDelegateCreator,
127+
JavaType arrayDelegateType,
128+
SettableBeanProperty[] arrayDelegateArgs)
129+
{
130+
_arrayDelegateCreator = arrayDelegateCreator;
131+
_arrayDelegateType = arrayDelegateType;
132+
_arrayDelegateArguments = arrayDelegateArgs;
133+
}
134+
115135
public void configureFromStringCreator(AnnotatedWithParams creator) {
116136
_fromStringCreator = creator;
117137
}
@@ -232,30 +252,17 @@ public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) t
232252
@Override
233253
public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) throws IOException
234254
{
235-
if (_delegateCreator == null) { // sanity-check; caller should check
236-
throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc());
237-
}
238-
try {
239-
// First simple case: just delegate, no injectables
240-
if (_delegateArguments == null) {
241-
return _delegateCreator.call1(delegate);
242-
}
243-
// And then the case with at least one injectable...
244-
final int len = _delegateArguments.length;
245-
Object[] args = new Object[len];
246-
for (int i = 0; i < len; ++i) {
247-
SettableBeanProperty prop = _delegateArguments[i];
248-
if (prop == null) { // delegate
249-
args[i] = delegate;
250-
} else { // nope, injectable:
251-
args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null);
252-
}
253-
}
254-
// and then try calling with full set of arguments
255-
return _delegateCreator.call(args);
256-
} catch (Throwable t) {
257-
throw rewrapCtorProblem(ctxt, t);
255+
return _createUsingDelegate(_delegateCreator, _delegateArguments, ctxt, delegate);
256+
}
257+
258+
@Override
259+
public Object createUsingArrayDelegate(DeserializationContext ctxt, Object delegate) throws IOException
260+
{
261+
if (_arrayDelegateCreator == null) { // sanity-check; caller should check
262+
// fallback to the classic delegate creator
263+
return createUsingDelegate(ctxt, delegate);
258264
}
265+
return _createUsingDelegate(_arrayDelegateCreator, _arrayDelegateArguments, ctxt, delegate);
259266
}
260267

261268
/*
@@ -441,4 +448,43 @@ protected JsonMappingException rewrapCtorProblem(DeserializationContext ctxt,
441448
}
442449
return wrapAsJsonMappingException(ctxt, t);
443450
}
451+
452+
/*
453+
/**********************************************************
454+
/* Helper methods
455+
/**********************************************************
456+
*/
457+
458+
private Object _createUsingDelegate(
459+
AnnotatedWithParams delegateCreator,
460+
SettableBeanProperty[] delegateArguments,
461+
DeserializationContext ctxt,
462+
Object delegate)
463+
throws IOException
464+
{
465+
if (delegateCreator == null) { // sanity-check; caller should check
466+
throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc());
467+
}
468+
try {
469+
// First simple case: just delegate, no injectables
470+
if (delegateArguments == null) {
471+
return delegateCreator.call1(delegate);
472+
}
473+
// And then the case with at least one injectable...
474+
final int len = delegateArguments.length;
475+
Object[] args = new Object[len];
476+
for (int i = 0; i < len; ++i) {
477+
SettableBeanProperty prop = delegateArguments[i];
478+
if (prop == null) { // delegate
479+
args[i] = delegate;
480+
} else { // nope, injectable:
481+
args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null);
482+
}
483+
}
484+
// and then try calling with full set of arguments
485+
return delegateCreator.call(args);
486+
} catch (Throwable t) {
487+
throw rewrapCtorProblem(ctxt, t);
488+
}
489+
}
444490
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.fasterxml.jackson.databind.deser;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.databind.BaseMapTest;
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
public class TestObjectOrArrayDeserialization extends BaseMapTest {
12+
13+
public static class ArrayOrObject {
14+
private final List<Object> objects;
15+
private final Object object;
16+
17+
@JsonCreator public ArrayOrObject(List<Object> objects) {
18+
this.objects = objects;
19+
this.object = null;
20+
}
21+
22+
@JsonCreator public ArrayOrObject(Object object) {
23+
this.objects = null;
24+
this.object = object;
25+
}
26+
}
27+
28+
public void testObjectCase() throws Exception {
29+
ArrayOrObject arrayOrObject = new ObjectMapper().readValue("{}", ArrayOrObject.class);
30+
assertNull("expected objects field to be null", arrayOrObject.objects);
31+
assertNotNull("expected object field not to be null", arrayOrObject.object);
32+
}
33+
34+
public void testEmptyArrayCase() throws Exception {
35+
ArrayOrObject arrayOrObject = new ObjectMapper().readValue("[]", ArrayOrObject.class);
36+
assertNotNull("expected objects field not to be null", arrayOrObject.objects);
37+
assertTrue("expected objects field to be an empty list", arrayOrObject.objects.isEmpty());
38+
assertNull("expected object field to be null", arrayOrObject.object);
39+
}
40+
41+
public void testNotEmptyArrayCase() throws Exception {
42+
ArrayOrObject arrayOrObject = new ObjectMapper().readValue("[{}, {}]", ArrayOrObject.class);
43+
assertNotNull("expected objects field not to be null", arrayOrObject.objects);
44+
assertEquals("expected objects field to have size 2", 2, arrayOrObject.objects.size());
45+
assertNull("expected object field to be null", arrayOrObject.object);
46+
}
47+
48+
}

0 commit comments

Comments
 (0)