blob: 7a6c39a55a93b7e444bddc32c74bdfcb2df16570 [file] [log] [blame]
package org.codehaus.jackson.map.deser.std;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.*;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.annotate.JacksonStdImpl;
import org.codehaus.jackson.map.deser.ValueInstantiator;
import org.codehaus.jackson.map.deser.std.ContainerDeserializerBase;
import org.codehaus.jackson.map.deser.std.StdValueInstantiator;
import org.codehaus.jackson.map.introspect.AnnotatedConstructor;
import org.codehaus.jackson.map.introspect.AnnotatedWithParams;
import org.codehaus.jackson.type.JavaType;
/**
* Basic serializer that can take JSON "Array" structure and
* construct a {@link java.util.Collection} instance, with typed contents.
*<p>
* Note: for untyped content (one indicated by passing Object.class
* as the type), {@link UntypedObjectDeserializer} is used instead.
* It can also construct {@link java.util.List}s, but not with specific
* POJO types, only other containers and primitives/wrappers.
*
* @since 1.9 (renamed from 'org.codehaus.jackson.map.deser.CollectionDeserializer)
*/
@JacksonStdImpl
public class CollectionDeserializer
extends ContainerDeserializerBase<Collection<Object>>
implements ResolvableDeserializer
{
// // Configuration
protected final JavaType _collectionType;
/**
* Value deserializer.
*/
protected final JsonDeserializer<Object> _valueDeserializer;
/**
* If element instances have polymorphic type information, this
* is the type deserializer that can handle it
*/
protected final TypeDeserializer _valueTypeDeserializer;
// // Instance construction settings:
/**
* @since 1.9
*/
protected final ValueInstantiator _valueInstantiator;
/**
* Deserializer that is used iff delegate-based creator is
* to be used for deserializing from JSON Object.
*/
protected JsonDeserializer<Object> _delegateDeserializer;
// NOTE: no PropertyBasedCreator, as JSON Arrays have no properties
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* @deprecated Since 1.9, use variant that takes ValueInstantiator
*/
@Deprecated
protected CollectionDeserializer(JavaType collectionType, JsonDeserializer<Object> valueDeser,
TypeDeserializer valueTypeDeser,
Constructor<Collection<Object>> defCtor)
{
super(collectionType.getRawClass());
_collectionType = collectionType;
_valueDeserializer = valueDeser;
_valueTypeDeserializer = valueTypeDeser;
// not super-clean, but has to do...
StdValueInstantiator inst = new StdValueInstantiator(null, collectionType);
if (defCtor != null) {
AnnotatedConstructor aCtor = new AnnotatedConstructor(defCtor,
null, null);
inst.configureFromObjectSettings(aCtor, null, null, null, null);
}
_valueInstantiator = inst;
}
/**
* @since 1.9
*/
public CollectionDeserializer(JavaType collectionType, JsonDeserializer<Object> valueDeser,
TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator)
{
super(collectionType.getRawClass());
_collectionType = collectionType;
_valueDeserializer = valueDeser;
_valueTypeDeserializer = valueTypeDeser;
_valueInstantiator = valueInstantiator;
}
/**
* Copy-constructor that can be used by sub-classes to allow
* copy-on-write styling copying of settings of an existing instance.
*
* @since 1.9
*/
protected CollectionDeserializer(CollectionDeserializer src)
{
super(src._valueClass);
_collectionType = src._collectionType;
_valueDeserializer = src._valueDeserializer;
_valueTypeDeserializer = src._valueTypeDeserializer;
_valueInstantiator = src._valueInstantiator;
_delegateDeserializer = src._delegateDeserializer;
}
/*
/**********************************************************
/* Validation, post-processing (ResolvableDeserializer)
/**********************************************************
*/
/**
* Method called to finalize setup of this deserializer,
* after deserializer itself has been registered. This
* is needed to handle recursive and transitive dependencies.
*/
@Override
public void resolve(DeserializationConfig config, DeserializerProvider provider)
throws JsonMappingException
{
// May need to resolve types for delegate-based creators:
if (_valueInstantiator.canCreateUsingDelegate()) {
JavaType delegateType = _valueInstantiator.getDelegateType();
if (delegateType == null) {
throw new IllegalArgumentException("Invalid delegate-creator definition for "+_collectionType
+": value instantiator ("+_valueInstantiator.getClass().getName()
+") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'");
}
AnnotatedWithParams delegateCreator = _valueInstantiator.getDelegateCreator();
// Need to create a temporary property to allow contextual deserializers:
// Note: unlike BeanDeserializer, we don't have an AnnotatedClass around; hence no annotations passed
BeanProperty.Std property = new BeanProperty.Std(null,
delegateType, null, delegateCreator);
_delegateDeserializer = findDeserializer(config, provider, delegateType, property);
}
}
/*
/**********************************************************
/* ContainerDeserializerBase API
/**********************************************************
*/
@Override
public JavaType getContentType() {
return _collectionType.getContentType();
}
@Override
public JsonDeserializer<Object> getContentDeserializer() {
return _valueDeserializer;
}
/*
/**********************************************************
/* JsonDeserializer API
/**********************************************************
*/
@SuppressWarnings("unchecked")
@Override
public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (_delegateDeserializer != null) {
return (Collection<Object>) _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt));
}
/* [JACKSON-620]: empty String may be ok; bit tricky to check, however, since
* there is also possibility of "auto-wrapping" of single-element arrays.
* Hence we only accept empty String here.
*/
if (jp.getCurrentToken() == JsonToken.VALUE_STRING) {
String str = jp.getText();
if (str.length() == 0) {
return (Collection<Object>) _valueInstantiator.createFromString(str);
}
}
return deserialize(jp, ctxt, (Collection<Object>) _valueInstantiator.createUsingDefault());
}
@Override
public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt,
Collection<Object> result)
throws IOException, JsonProcessingException
{
// Ok: must point to START_ARRAY (or equivalent)
if (!jp.isExpectedStartArrayToken()) {
return handleNonArray(jp, ctxt, result);
}
JsonDeserializer<Object> valueDes = _valueDeserializer;
JsonToken t;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
Object value;
if (t == JsonToken.VALUE_NULL) {
value = null;
} else if (typeDeser == null) {
value = valueDes.deserialize(jp, ctxt);
} else {
value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
}
result.add(value);
}
return result;
}
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
// In future could check current token... for now this should be enough:
return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
}
/**
* Helper method called when current token is no START_ARRAY. Will either
* throw an exception, or try to handle value as if member of implicit
* array, depending on configuration.
*/
private final Collection<Object> handleNonArray(JsonParser jp, DeserializationContext ctxt,
Collection<Object> result)
throws IOException, JsonProcessingException
{
// [JACKSON-526]: implicit arrays from single values?
if (!ctxt.isEnabled(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
throw ctxt.mappingException(_collectionType.getRawClass());
}
JsonDeserializer<Object> valueDes = _valueDeserializer;
final TypeDeserializer typeDeser = _valueTypeDeserializer;
JsonToken t = jp.getCurrentToken();
Object value;
if (t == JsonToken.VALUE_NULL) {
value = null;
} else if (typeDeser == null) {
value = valueDes.deserialize(jp, ctxt);
} else {
value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
}
result.add(value);
return result;
}
}