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_OBJECT} * inclusion mechanism. Simple since JSON structure used is always * the same, regardless of structure used for actual value: wrapping * is done using a single-element JSON Object where type id is the key, * and actual object data as the value. * * @author tatu */ public class AsWrapperTypeDeserializer extends TypeDeserializerBase implements java.io.Serializable { private static final long serialVersionUID = 5345570420394408290L; public AsWrapperTypeDeserializer(JavaType bt, TypeIdResolver idRes, String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl) { super(bt, idRes, typePropertyName, typeIdVisible, null); } protected AsWrapperTypeDeserializer(AsWrapperTypeDeserializer src, BeanProperty property) { super(src, property); } @Override public TypeDeserializer forProperty(BeanProperty prop) { if (prop == _property) { // usually if it's null return this; } return new AsWrapperTypeDeserializer(this, prop); } @Override public As getTypeInclusion() { return As.WRAPPER_OBJECT; } /** * Deserializing type id enclosed using WRAPPER_OBJECT style is straightforward */ @Override public Object deserializeTypedFromObject(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { return _deserialize(jp, ctxt); } @Override public Object deserializeTypedFromArray(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 { // first, sanity checks if (jp.getCurrentToken() != JsonToken.START_OBJECT) { throw ctxt.wrongTokenException(jp, JsonToken.START_OBJECT, "need JSON Object to contain As.WRAPPER_OBJECT type information for class "+baseTypeName()); } // should always get field name, but just in case... if (jp.nextToken() != JsonToken.FIELD_NAME) { throw ctxt.wrongTokenException(jp, JsonToken.FIELD_NAME, "need JSON String that contains type id (for subtype of "+baseTypeName()+")"); } final String typeId = jp.getText(); JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId); jp.nextToken(); // 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_OBJECT if (jp.nextToken() != JsonToken.END_OBJECT) { throw ctxt.wrongTokenException(jp, JsonToken.END_OBJECT, "expected closing END_OBJECT after type information and deserialized value"); } return value; } }