| 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; |
| } |
| |
| } |