package com.fasterxml.jackson.databind.deser.std; import java.io.IOException; import java.lang.reflect.Array; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; import com.fasterxml.jackson.databind.deser.ContextualDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.type.ArrayType; import com.fasterxml.jackson.databind.util.ObjectBuffer; /** * Basic serializer that can serialize non-primitive arrays. */ @JacksonStdImpl public class ObjectArrayDeserializer extends ContainerDeserializerBase<Object[]> implements ContextualDeserializer { // // Configuration /** * Full generic type of the array being deserialized */ protected final ArrayType _arrayType; /** * Flag that indicates whether the component type is Object or not. * Used for minor optimization when constructing result. */ protected final boolean _untyped; /** * Type of contained elements: needed for constructing actual * result array */ protected final Class<?> _elementClass; /** * Element deserializer */ protected JsonDeserializer<Object> _elementDeserializer; /** * If element instances have polymorphic type information, this * is the type deserializer that can handle it */ protected final TypeDeserializer _elementTypeDeserializer; /* /********************************************************** /* Life-cycle /********************************************************** */ public ObjectArrayDeserializer(ArrayType arrayType, JsonDeserializer<Object> elemDeser, TypeDeserializer elemTypeDeser) { super(Object[].class); _arrayType = arrayType; _elementClass = arrayType.getContentType().getRawClass(); _untyped = (_elementClass == Object.class); _elementDeserializer = elemDeser; _elementTypeDeserializer = elemTypeDeser; } /** * Overridable fluent-factory method used to create contextual instances */ @SuppressWarnings("unchecked") public ObjectArrayDeserializer withDeserializer(TypeDeserializer elemTypeDeser, JsonDeserializer<?> elemDeser) { if ((elemDeser == _elementDeserializer) && (elemTypeDeser == _elementTypeDeserializer)) { return this; } return new ObjectArrayDeserializer(_arrayType, (JsonDeserializer<Object>) elemDeser, elemTypeDeser); } // @Override public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { JsonDeserializer<?> deser = _elementDeserializer; if (deser == null) { deser = ctxt.findContextualValueDeserializer(_arrayType.getContentType(), property); } else { // if directly assigned, probably not yet contextual, so: if (deser instanceof ContextualDeserializer) { deser = ((ContextualDeserializer) deser).createContextual(ctxt, property); } } TypeDeserializer elemTypeDeser = _elementTypeDeserializer; if (elemTypeDeser != null) { elemTypeDeser = elemTypeDeser.forProperty(property); } return withDeserializer(elemTypeDeser, deser); } /* /********************************************************** /* ContainerDeserializerBase API /********************************************************** */ @Override public JavaType getContentType() { return _arrayType.getContentType(); } @Override public JsonDeserializer<Object> getContentDeserializer() { return _elementDeserializer; } /* /********************************************************** /* JsonDeserializer API /********************************************************** */ @Override public Object[] deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { // Ok: must point to START_ARRAY (or equivalent) if (!jp.isExpectedStartArrayToken()) { return handleNonArray(jp, ctxt); } final ObjectBuffer buffer = ctxt.leaseObjectBuffer(); Object[] chunk = buffer.resetAndStart(); int ix = 0; JsonToken t; final TypeDeserializer typeDeser = _elementTypeDeserializer; while ((t = jp.nextToken()) != JsonToken.END_ARRAY) { // Note: must handle null explicitly here; value deserializers won't Object value; if (t == JsonToken.VALUE_NULL) { value = null; } else if (typeDeser == null) { value = _elementDeserializer.deserialize(jp, ctxt); } else { value = _elementDeserializer.deserializeWithType(jp, ctxt, typeDeser); } if (ix >= chunk.length) { chunk = buffer.appendCompletedChunk(chunk); ix = 0; } chunk[ix++] = value; } Object[] result; if (_untyped) { result = buffer.completeAndClearBuffer(chunk, ix); } else { result = buffer.completeAndClearBuffer(chunk, ix, _elementClass); } ctxt.returnObjectBuffer(buffer); return result; } @Override public Object[] deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException, JsonProcessingException { /* Should there be separate handling for base64 stuff? * for now this should be enough: */ return (Object[]) typeDeserializer.deserializeTypedFromArray(jp, ctxt); } /* /********************************************************** /* Internal methods /********************************************************** */ protected Byte[] deserializeFromBase64(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { // First same as what PrimitiveArrayDeserializers.ByteDeser does: byte[] b = jp.getBinaryValue(ctxt.getBase64Variant()); // But then need to convert to wrappers Byte[] result = new Byte[b.length]; for (int i = 0, len = b.length; i < len; ++i) { result[i] = Byte.valueOf(b[i]); } return result; } private final Object[] handleNonArray(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { // [JACKSON-620] Empty String can become null... if ((jp.getCurrentToken() == JsonToken.VALUE_STRING) && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) { String str = jp.getText(); if (str.length() == 0) { return null; } } // Can we do implicit coercion to a single-element array still? if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) { /* 04-Oct-2009, tatu: One exception; byte arrays are generally * serialized as base64, so that should be handled */ if (jp.getCurrentToken() == JsonToken.VALUE_STRING && _elementClass == Byte.class) { return deserializeFromBase64(jp, ctxt); } throw ctxt.mappingException(_arrayType.getRawClass()); } JsonToken t = jp.getCurrentToken(); Object value; if (t == JsonToken.VALUE_NULL) { value = null; } else if (_elementTypeDeserializer == null) { value = _elementDeserializer.deserialize(jp, ctxt); } else { value = _elementDeserializer.deserializeWithType(jp, ctxt, _elementTypeDeserializer); } // Ok: bit tricky, since we may want T[], not just Object[] Object[] result; if (_untyped) { result = new Object[1]; } else { result = (Object[]) Array.newInstance(_elementClass, 1); } result[0] = value; return result; } }