Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,7 @@ public Object deserializeFromArray(JsonParser p, DeserializationContext ctxt) th
{
if (_delegateDeserializer != null) {
try {
Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
Object bean = _valueInstantiator.createUsingArrayDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
if (_injectables != null) {
injectValues(ctxt, bean);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,15 @@ public Object createUsingDelegate(DeserializationContext ctxt, Object delegate)
getValueTypeDesc());
}

/**
* Method to called to create value instance from JSON Array using
* an intermediate "delegate" value to pass to createor method
*/
public Object createUsingArrayDelegate(DeserializationContext ctxt, Object delegate) throws IOException {
throw ctxt.mappingException("Can not instantiate value of type %s using delegate",
getValueTypeDesc());
}

/*
/**********************************************************
/* Instantiation methods for JSON scalar types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class CreatorCollector
protected final static int C_BOOLEAN = 5;
protected final static int C_DELEGATE = 6;
protected final static int C_PROPS = 7;
protected final static int C_ARRAY_DELEGATE = 8;

protected final static String[] TYPE_DESCS = new String[] {
"default",
Expand All @@ -50,7 +51,7 @@ public class CreatorCollector
*
* @since 2.5
*/
protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[8];
protected final AnnotatedWithParams[] _creators = new AnnotatedWithParams[9];

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

protected SettableBeanProperty[] _arrayDelegateArgs;

protected SettableBeanProperty[] _propertyBasedArgs;

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

public ValueInstantiator constructValueInstantiator(DeserializationConfig config)
{
JavaType delegateType;
boolean maybeVanilla = !_hasNonDefaultCreator;

if (maybeVanilla || (_creators[C_DELEGATE] == null)) {
delegateType = null;
} else {
// need to find type...
int ix = 0;
if (_delegateArgs != null) {
for (int i = 0, len = _delegateArgs.length; i < len; ++i) {
if (_delegateArgs[i] == null) { // marker for delegate itself
ix = i;
break;
}
}
}
delegateType = _creators[C_DELEGATE].getParameterType(ix);
}

final JavaType delegateType = _computeDelegateType(_creators[C_DELEGATE], _delegateArgs);
final JavaType arrayDelegateType = _computeDelegateType(_creators[C_ARRAY_DELEGATE], _arrayDelegateArgs);
final JavaType type = _beanDesc.getType();

// Any non-standard creator will prevent; with one exception: int-valued constructor
// that standard containers have can be ignored
maybeVanilla &= !_hasNonDefaultCreator;

if (maybeVanilla) {
if (!_hasNonDefaultCreator) {
/* 10-May-2014, tatu: If we have nothing special, and we are dealing with one
* of "well-known" types, can create a non-reflection-based instantiator.
*/
Expand All @@ -129,6 +113,7 @@ public ValueInstantiator constructValueInstantiator(DeserializationConfig config
inst.configureFromObjectSettings(_creators[C_DEFAULT],
_creators[C_DELEGATE], delegateType, _delegateArgs,
_creators[C_PROPS], _propertyBasedArgs);
inst.configureFromArraySettings(_creators[C_ARRAY_DELEGATE], arrayDelegateType, _arrayDelegateArgs);
inst.configureFromStringCreator(_creators[C_STRING]);
inst.configureFromIntCreator(_creators[C_INT]);
inst.configureFromLongCreator(_creators[C_LONG]);
Expand Down Expand Up @@ -176,8 +161,13 @@ public void addBooleanCreator(AnnotatedWithParams creator, boolean explicit) {
public void addDelegatingCreator(AnnotatedWithParams creator, boolean explicit,
SettableBeanProperty[] injectables)
{
verifyNonDup(creator, C_DELEGATE, explicit);
_delegateArgs = injectables;
if (creator.getParameterType(0).isCollectionLikeType()) {
verifyNonDup(creator, C_ARRAY_DELEGATE, explicit);
_arrayDelegateArgs = injectables;
} else {
verifyNonDup(creator, C_DELEGATE, explicit);
_delegateArgs = injectables;
}
}

public void addPropertyCreator(AnnotatedWithParams creator, boolean explicit,
Expand Down Expand Up @@ -276,6 +266,25 @@ public boolean hasPropertyBasedCreator() {
/**********************************************************
*/

private JavaType _computeDelegateType(AnnotatedWithParams creator, SettableBeanProperty[] delegateArgs)
{
if (!_hasNonDefaultCreator || (creator == null)) {
return null;
} else {
// need to find type...
int ix = 0;
if (delegateArgs != null) {
for (int i = 0, len = delegateArgs.length; i < len; ++i) {
if (delegateArgs[i] == null) { // marker for delegate itself
ix = i;
break;
}
}
}
return creator.getParameterType(ix);
}
}

private <T extends AnnotatedMember> T _fixAccess(T member)
{
if (member != null && _canFixAccess) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public class StdValueInstantiator
protected JavaType _delegateType;
protected AnnotatedWithParams _delegateCreator;
protected SettableBeanProperty[] _delegateArguments;

// // // Array delegate construction

protected JavaType _arrayDelegateType;
protected AnnotatedWithParams _arrayDelegateCreator;
protected SettableBeanProperty[] _arrayDelegateArguments;

// // // Scalar construction

Expand Down Expand Up @@ -87,6 +93,10 @@ protected StdValueInstantiator(StdValueInstantiator src)
_delegateType = src._delegateType;
_delegateCreator = src._delegateCreator;
_delegateArguments = src._delegateArguments;

_arrayDelegateType = src._arrayDelegateType;
_arrayDelegateCreator = src._arrayDelegateCreator;
_arrayDelegateArguments = src._arrayDelegateArguments;

_fromStringCreator = src._fromStringCreator;
_fromIntCreator = src._fromIntCreator;
Expand All @@ -112,6 +122,16 @@ public void configureFromObjectSettings(AnnotatedWithParams defaultCreator,
_constructorArguments = constructorArgs;
}

public void configureFromArraySettings(
AnnotatedWithParams arrayDelegateCreator,
JavaType arrayDelegateType,
SettableBeanProperty[] arrayDelegateArgs)
{
_arrayDelegateCreator = arrayDelegateCreator;
_arrayDelegateType = arrayDelegateType;
_arrayDelegateArguments = arrayDelegateArgs;
}

public void configureFromStringCreator(AnnotatedWithParams creator) {
_fromStringCreator = creator;
}
Expand Down Expand Up @@ -232,30 +252,17 @@ public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) t
@Override
public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) throws IOException
{
if (_delegateCreator == null) { // sanity-check; caller should check
throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc());
}
try {
// First simple case: just delegate, no injectables
if (_delegateArguments == null) {
return _delegateCreator.call1(delegate);
}
// And then the case with at least one injectable...
final int len = _delegateArguments.length;
Object[] args = new Object[len];
for (int i = 0; i < len; ++i) {
SettableBeanProperty prop = _delegateArguments[i];
if (prop == null) { // delegate
args[i] = delegate;
} else { // nope, injectable:
args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null);
}
}
// and then try calling with full set of arguments
return _delegateCreator.call(args);
} catch (Throwable t) {
throw rewrapCtorProblem(ctxt, t);
return _createUsingDelegate(_delegateCreator, _delegateArguments, ctxt, delegate);
}

@Override
public Object createUsingArrayDelegate(DeserializationContext ctxt, Object delegate) throws IOException
{
if (_arrayDelegateCreator == null) { // sanity-check; caller should check
// fallback to the classic delegate creator
return createUsingDelegate(ctxt, delegate);
}
return _createUsingDelegate(_arrayDelegateCreator, _arrayDelegateArguments, ctxt, delegate);
}

/*
Expand Down Expand Up @@ -441,4 +448,43 @@ protected JsonMappingException rewrapCtorProblem(DeserializationContext ctxt,
}
return wrapAsJsonMappingException(ctxt, t);
}

/*
/**********************************************************
/* Helper methods
/**********************************************************
*/

private Object _createUsingDelegate(
AnnotatedWithParams delegateCreator,
SettableBeanProperty[] delegateArguments,
DeserializationContext ctxt,
Object delegate)
throws IOException
{
if (delegateCreator == null) { // sanity-check; caller should check
throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc());
}
try {
// First simple case: just delegate, no injectables
if (delegateArguments == null) {
return delegateCreator.call1(delegate);
}
// And then the case with at least one injectable...
final int len = delegateArguments.length;
Object[] args = new Object[len];
for (int i = 0; i < len; ++i) {
SettableBeanProperty prop = delegateArguments[i];
if (prop == null) { // delegate
args[i] = delegate;
} else { // nope, injectable:
args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null);
}
}
// and then try calling with full set of arguments
return delegateCreator.call(args);
} catch (Throwable t) {
throw rewrapCtorProblem(ctxt, t);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.fasterxml.jackson.databind.deser;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.List;
import java.util.Map;

public class TestObjectOrArrayDeserialization extends BaseMapTest {

public static class ArrayOrObject {
private final List<Object> objects;
private final Object object;

@JsonCreator public ArrayOrObject(List<Object> objects) {
this.objects = objects;
this.object = null;
}

@JsonCreator public ArrayOrObject(Object object) {
this.objects = null;
this.object = object;
}
}

public void testObjectCase() throws Exception {
ArrayOrObject arrayOrObject = new ObjectMapper().readValue("{}", ArrayOrObject.class);
assertNull("expected objects field to be null", arrayOrObject.objects);
assertNotNull("expected object field not to be null", arrayOrObject.object);
}

public void testEmptyArrayCase() throws Exception {
ArrayOrObject arrayOrObject = new ObjectMapper().readValue("[]", ArrayOrObject.class);
assertNotNull("expected objects field not to be null", arrayOrObject.objects);
assertTrue("expected objects field to be an empty list", arrayOrObject.objects.isEmpty());
assertNull("expected object field to be null", arrayOrObject.object);
}

public void testNotEmptyArrayCase() throws Exception {
ArrayOrObject arrayOrObject = new ObjectMapper().readValue("[{}, {}]", ArrayOrObject.class);
assertNotNull("expected objects field not to be null", arrayOrObject.objects);
assertEquals("expected objects field to have size 2", 2, arrayOrObject.objects.size());
assertNull("expected object field to be null", arrayOrObject.object);
}

}