| 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. |
| */ |
| public class AsWrapperTypeDeserializer |
| extends TypeDeserializerBase |
| implements java.io.Serializable |
| { |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * @since 2.8 |
| */ |
| public AsWrapperTypeDeserializer(JavaType bt, TypeIdResolver idRes, |
| String typePropertyName, boolean typeIdVisible, JavaType defaultImpl) |
| { |
| super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl); |
| } |
| |
| protected AsWrapperTypeDeserializer(AsWrapperTypeDeserializer src, BeanProperty property) { |
| super(src, property); |
| } |
| |
| @Override |
| public TypeDeserializer forProperty(BeanProperty prop) { |
| return (prop == _property) ? this : 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 { |
| return _deserialize(jp, ctxt); |
| } |
| |
| @Override |
| public Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt) throws IOException { |
| return _deserialize(jp, ctxt); |
| } |
| |
| @Override |
| public Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt) throws IOException { |
| return _deserialize(jp, ctxt); |
| } |
| |
| @Override |
| public Object deserializeTypedFromAny(JsonParser jp, DeserializationContext ctxt) throws IOException { |
| 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. |
| */ |
| @SuppressWarnings("resource") |
| protected Object _deserialize(JsonParser p, DeserializationContext ctxt) throws IOException |
| { |
| // 02-Aug-2013, tatu: May need to use native type ids |
| if (p.canReadTypeId()) { |
| Object typeId = p.getTypeId(); |
| if (typeId != null) { |
| return _deserializeWithNativeTypeId(p, ctxt, typeId); |
| } |
| } |
| // first, sanity checks |
| JsonToken t = p.getCurrentToken(); |
| if (t == JsonToken.START_OBJECT) { |
| // should always get field name, but just in case... |
| if (p.nextToken() != JsonToken.FIELD_NAME) { |
| ctxt.reportWrongTokenException(baseType(), JsonToken.FIELD_NAME, |
| "need JSON String that contains type id (for subtype of "+baseTypeName()+")"); |
| } |
| } else if (t != JsonToken.FIELD_NAME) { |
| ctxt.reportWrongTokenException(baseType(), JsonToken.START_OBJECT, |
| "need JSON Object to contain As.WRAPPER_OBJECT type information for class "+baseTypeName()); |
| } |
| final String typeId = p.getText(); |
| JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId); |
| p.nextToken(); |
| |
| // Minor complication: we may need to merge type id in? |
| if (_typeIdVisible && p.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, false); |
| tb.writeStartObject(); // recreate START_OBJECT |
| tb.writeFieldName(_typePropertyName); |
| tb.writeString(typeId); |
| // 02-Jul-2016, tatu: Depending on for JsonParserSequence is initialized it may |
| // try to access current token; ensure there isn't one |
| p.clearCurrentToken(); |
| p = JsonParserSequence.createFlattened(false, tb.asParser(p), p); |
| p.nextToken(); |
| } |
| |
| Object value = deser.deserialize(p, ctxt); |
| // And then need the closing END_OBJECT |
| if (p.nextToken() != JsonToken.END_OBJECT) { |
| ctxt.reportWrongTokenException(baseType(), JsonToken.END_OBJECT, |
| "expected closing END_OBJECT after type information and deserialized value"); |
| } |
| return value; |
| } |
| } |