1 package com.fasterxml.jackson.databind.jsontype; 2 3 import java.io.IOException; 4 5 import com.fasterxml.jackson.core.*; 6 import com.fasterxml.jackson.databind.BeanProperty; 7 import com.fasterxml.jackson.databind.DeserializationContext; 8 import com.fasterxml.jackson.databind.JavaType; 9 import com.fasterxml.jackson.databind.JsonDeserializer; 10 import com.fasterxml.jackson.annotation.JsonTypeInfo.As; 11 12 13 /** 14 * Interface for deserializing type information from JSON content, to 15 * type-safely deserialize data into correct polymorphic instance 16 * (when type inclusion has been enabled for type handled). 17 *<p> 18 * Separate deserialization methods are needed because serialized 19 * form for inclusion mechanism {@link As#PROPERTY} 20 * is slighty different if value is not expressed as JSON Object: 21 * and as such both type deserializer and serializer need to 22 * JSON Object form (array, object or other (== scalar)) being used. 23 */ 24 public abstract class TypeDeserializer 25 { 26 /* 27 /********************************************************** 28 /* Initialization 29 /********************************************************** 30 */ 31 32 /** 33 * Method called to create contextual version, to be used for 34 * values of given property. This may be the type itself 35 * (as is the case for bean properties), or values contained 36 * (for {@link java.util.Collection} or {@link java.util.Map} 37 * valued properties). 38 */ forProperty(BeanProperty prop)39 public abstract TypeDeserializer forProperty(BeanProperty prop); 40 41 /* 42 /********************************************************** 43 /* Introspection 44 /********************************************************** 45 */ 46 47 /** 48 * Accessor for type information inclusion method 49 * that deserializer uses; indicates how type information 50 * is (expected to be) embedded in JSON input. 51 */ getTypeInclusion()52 public abstract As getTypeInclusion(); 53 54 /** 55 * Name of property that contains type information, if 56 * property-based inclusion is used. 57 */ getPropertyName()58 public abstract String getPropertyName(); 59 60 /** 61 * Accessor for object that handles conversions between 62 * types and matching type ids. 63 */ getTypeIdResolver()64 public abstract TypeIdResolver getTypeIdResolver(); 65 66 /** 67 * Accessor for "default implementation" type; optionally defined 68 * class to use in cases where type id is not 69 * accessible for some reason (either missing, or cannot be 70 * resolved) 71 */ getDefaultImpl()72 public abstract Class<?> getDefaultImpl(); 73 74 /* 75 /********************************************************** 76 /* Type deserialization methods 77 /********************************************************** 78 */ 79 80 /** 81 * Method called to let this type deserializer handle 82 * deserialization of "typed" object, when value itself 83 * is serialized as JSON Object (regardless of Java type). 84 * Method needs to figure out intended 85 * polymorphic type, locate {@link JsonDeserializer} to use, and 86 * call it with JSON data to deserializer (which does not contain 87 * type information). 88 */ deserializeTypedFromObject(JsonParser p, DeserializationContext ctxt)89 public abstract Object deserializeTypedFromObject(JsonParser p, DeserializationContext ctxt) throws IOException; 90 91 /** 92 * Method called to let this type deserializer handle 93 * deserialization of "typed" object, when value itself 94 * is serialized as JSON Array (regardless of Java type). 95 * Method needs to figure out intended 96 * polymorphic type, locate {@link JsonDeserializer} to use, and 97 * call it with JSON data to deserializer (which does not contain 98 * type information). 99 */ deserializeTypedFromArray(JsonParser p, DeserializationContext ctxt)100 public abstract Object deserializeTypedFromArray(JsonParser p, DeserializationContext ctxt) throws IOException; 101 102 /** 103 * Method called to let this type deserializer handle 104 * deserialization of "typed" object, when value itself 105 * is serialized as a scalar JSON value (something other 106 * than Array or Object), regardless of Java type. 107 * Method needs to figure out intended 108 * polymorphic type, locate {@link JsonDeserializer} to use, and 109 * call it with JSON data to deserializer (which does not contain 110 * type information). 111 */ deserializeTypedFromScalar(JsonParser p, DeserializationContext ctxt)112 public abstract Object deserializeTypedFromScalar(JsonParser p, DeserializationContext ctxt) throws IOException; 113 114 /** 115 * Method called to let this type deserializer handle 116 * deserialization of "typed" object, when value itself 117 * may have been serialized using any kind of JSON value 118 * (Array, Object, scalar). Should only be called if JSON 119 * serialization is polymorphic (not Java type); for example when 120 * using JSON node representation, or "untyped" Java object 121 * (which may be Map, Collection, wrapper/primitive etc). 122 */ deserializeTypedFromAny(JsonParser p, DeserializationContext ctxt)123 public abstract Object deserializeTypedFromAny(JsonParser p, DeserializationContext ctxt) throws IOException; 124 125 /* 126 /********************************************************** 127 /* Shared helper methods 128 /********************************************************** 129 */ 130 131 /** 132 * Helper method used to check if given parser might be pointing to 133 * a "natural" value, and one that would be acceptable as the 134 * result value (compatible with declared base type) 135 */ deserializeIfNatural(JsonParser p, DeserializationContext ctxt, JavaType baseType)136 public static Object deserializeIfNatural(JsonParser p, DeserializationContext ctxt, JavaType baseType) throws IOException { 137 return deserializeIfNatural(p, ctxt, baseType.getRawClass()); 138 } 139 140 @SuppressWarnings("incomplete-switch") deserializeIfNatural(JsonParser p, DeserializationContext ctxt, Class<?> base)141 public static Object deserializeIfNatural(JsonParser p, DeserializationContext ctxt, 142 Class<?> base) throws IOException 143 { 144 final JsonToken t = p.currentToken(); 145 if (t == null) { 146 return null; 147 } 148 switch (t) { 149 case VALUE_STRING: 150 if (base.isAssignableFrom(String.class)) { 151 return p.getText(); 152 } 153 break; 154 case VALUE_NUMBER_INT: 155 if (base.isAssignableFrom(Integer.class)) { 156 return p.getIntValue(); 157 } 158 break; 159 160 case VALUE_NUMBER_FLOAT: 161 if (base.isAssignableFrom(Double.class)) { 162 return Double.valueOf(p.getDoubleValue()); 163 } 164 break; 165 case VALUE_TRUE: 166 if (base.isAssignableFrom(Boolean.class)) { 167 return Boolean.TRUE; 168 } 169 break; 170 case VALUE_FALSE: 171 if (base.isAssignableFrom(Boolean.class)) { 172 return Boolean.FALSE; 173 } 174 break; 175 } 176 return null; 177 } 178 } 179