package com.fasterxml.jackson.databind.jsontype.impl;
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.JsonParserSequence;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.util.TokenBuffer;
/**
* Type deserializer used with {@link As#WRAPPER_ARRAY}
* inclusion mechanism. Simple since JSON structure used is always
* the same, regardless of structure used for actual value: wrapping
* is done using a 2-element JSON Array where type id is the first
* element, and actual object data as second element.
*
* @author tatu
*/
public class AsArrayTypeDeserializer
extends TypeDeserializerBase
implements java.io.Serializable
{
private static final long serialVersionUID = 5345570420394408290L;
public AsArrayTypeDeserializer(JavaType bt, TypeIdResolver idRes,
String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
{
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
}
public AsArrayTypeDeserializer(AsArrayTypeDeserializer src, BeanProperty property) {
super(src, property);
}
@Override
public TypeDeserializer forProperty(BeanProperty prop) {
if (prop == _property) { // usually if it's null
return this;
}
return new AsArrayTypeDeserializer(this, prop);
}
@Override
public As getTypeInclusion() {
return As.WRAPPER_ARRAY;
}
/**
* Method called when actual object is serialized as JSON Array.
*/
@Override
public Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return _deserialize(jp, ctxt);
}
/**
* Method called when actual object is serialized as JSON Object
*/
@Override
public Object deserializeTypedFromObject(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return _deserialize(jp, ctxt);
}
@Override
public Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return _deserialize(jp, ctxt);
}
@Override
public Object deserializeTypedFromAny(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return _deserialize(jp, ctxt);
}
/*
/***************************************************************
/* Internal methods
/***************************************************************
*/
/**
* Method that handles type information wrapper, locates actual
* subtype deserializer to use, and calls it to do actual
* deserialization.
*/
private final Object _deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
boolean hadStartArray = jp.isExpectedStartArrayToken();
String typeId = _locateTypeId(jp, ctxt);
JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId);
// Minor complication: we may need to merge type id in?
if (_typeIdVisible && jp.getCurrentToken() == JsonToken.START_OBJECT) {
// but what if there's nowhere to add it in? Error? Or skip? For now, skip.
TokenBuffer tb = new TokenBuffer(null);
tb.writeStartObject(); // recreate START_OBJECT
tb.writeFieldName(_typePropertyName);
tb.writeString(typeId);
jp = JsonParserSequence.createFlattened(tb.asParser(jp), jp);
jp.nextToken();
}
Object value = deser.deserialize(jp, ctxt);
// And then need the closing END_ARRAY
if (hadStartArray && jp.nextToken() != JsonToken.END_ARRAY) {
throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
"expected closing END_ARRAY after type information and deserialized value");
}
return value;
}
protected final String _locateTypeId(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (!jp.isExpectedStartArrayToken()) {
// [JACKSON-712] Need to allow even more customized handling, if something unexpected seen...
// but should there be a way to limit this to likely success cases?
if (_defaultImpl != null) {
return _idResolver.idFromBaseType();
}
throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "need JSON Array to contain As.WRAPPER_ARRAY type information for class "+baseTypeName());
}
// And then type id as a String
if (jp.nextToken() != JsonToken.VALUE_STRING) {
if (_defaultImpl != null) {
return _idResolver.idFromBaseType();
}
throw ctxt.wrongTokenException(jp, JsonToken.VALUE_STRING, "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
}
String result = jp.getText();
jp.nextToken();
return result;
}
}