package org.codehaus.jackson.map.deser;
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.DeserializationConfig;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.TypeDeserializer;
import org.codehaus.jackson.map.annotate.JacksonStdImpl;
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.
*/
@JacksonStdImpl
public class CollectionDeserializer
extends ContainerDeserializer<Collection<Object>>
{
// // Configuration
protected final JavaType _collectionType;
/**
* Value deserializer.
*/
final JsonDeserializer<Object> _valueDeserializer;
/**
* If element instances have polymorphic type information, this
* is the type deserializer that can handle it
*/
final TypeDeserializer _valueTypeDeserializer;
/**
* We will use the default constructor of the class for
* instantiation
*/
final Constructor<Collection<Object>> _defaultCtor;
public CollectionDeserializer(JavaType collectionType, JsonDeserializer<Object> valueDeser,
TypeDeserializer valueTypeDeser,
Constructor<Collection<Object>> ctor)
{
super(collectionType.getRawClass());
_collectionType = collectionType;
_valueDeserializer = valueDeser;
_valueTypeDeserializer = valueTypeDeser;
if (ctor == null) {
throw new IllegalArgumentException("No default constructor found for container class "+collectionType.getRawClass().getName());
}
_defaultCtor = ctor;
}
/*
/**********************************************************
/* ContainerDeserializer API
/**********************************************************
*/
@Override
public JavaType getContentType() {
return _collectionType.getContentType();
}
@Override
public JsonDeserializer<Object> getContentDeserializer() {
return _valueDeserializer;
}
/*
/**********************************************************
/* JsonDeserializer API
/**********************************************************
*/
@Override
public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
Collection<Object> result;
try {
result = _defaultCtor.newInstance();
} catch (Exception e) {
throw ctxt.instantiationException(_collectionType.getRawClass(), e);
}
return deserialize(jp, ctxt, result);
}
@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;
}
}