1 package com.fasterxml.jackson.databind.deser; 2 3 import java.io.IOException; 4 import java.util.*; 5 6 import com.fasterxml.jackson.annotation.ObjectIdGenerator; 7 import com.fasterxml.jackson.annotation.ObjectIdGenerators; 8 import com.fasterxml.jackson.annotation.ObjectIdResolver; 9 10 import com.fasterxml.jackson.core.*; 11 12 import com.fasterxml.jackson.databind.*; 13 import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; 14 import com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator; 15 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId; 16 import com.fasterxml.jackson.databind.introspect.AnnotatedMember; 17 import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; 18 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; 19 import com.fasterxml.jackson.databind.type.LogicalType; 20 21 /** 22 * Deserializer only used for abstract types used as placeholders during polymorphic 23 * type handling deserialization. If so, there is no real deserializer associated 24 * with nominal type, just {@link TypeDeserializer}; and any calls that do not 25 * pass such resolver will result in an error. 26 */ 27 public class AbstractDeserializer 28 extends JsonDeserializer<Object> 29 implements ContextualDeserializer, // since 2.9 30 java.io.Serializable 31 { 32 private static final long serialVersionUID = 1L; 33 34 protected final JavaType _baseType; 35 36 protected final ObjectIdReader _objectIdReader; 37 38 protected final Map<String, SettableBeanProperty> _backRefProperties; 39 40 protected transient Map<String,SettableBeanProperty> _properties; 41 42 // support for "native" types, which require special care: 43 44 protected final boolean _acceptString; 45 protected final boolean _acceptBoolean; 46 protected final boolean _acceptInt; 47 protected final boolean _acceptDouble; 48 49 /* 50 /********************************************************** 51 /* Life cycle 52 /********************************************************** 53 */ 54 55 /** 56 * @since 2.9 57 * 58 * @param props Regular properties: currently only needed to support property-annotated 59 * Object Id handling with property inclusion (needed for determining type of Object Id 60 * to bind) 61 */ AbstractDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps, Map<String, SettableBeanProperty> props)62 public AbstractDeserializer(BeanDeserializerBuilder builder, 63 BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps, 64 Map<String, SettableBeanProperty> props) 65 { 66 _baseType = beanDesc.getType(); 67 _objectIdReader = builder.getObjectIdReader(); 68 _backRefProperties = backRefProps; 69 _properties = props; 70 Class<?> cls = _baseType.getRawClass(); 71 _acceptString = cls.isAssignableFrom(String.class); 72 _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class); 73 _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class); 74 _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); 75 } 76 77 @Deprecated // since 2.9 AbstractDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps)78 public AbstractDeserializer(BeanDeserializerBuilder builder, 79 BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps) { 80 this(builder, beanDesc, backRefProps, null); 81 } 82 AbstractDeserializer(BeanDescription beanDesc)83 protected AbstractDeserializer(BeanDescription beanDesc) 84 { 85 _baseType = beanDesc.getType(); 86 _objectIdReader = null; 87 _backRefProperties = null; 88 Class<?> cls = _baseType.getRawClass(); 89 _acceptString = cls.isAssignableFrom(String.class); 90 _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class); 91 _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class); 92 _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); 93 } 94 95 /** 96 * @since 2.9 97 */ AbstractDeserializer(AbstractDeserializer base, ObjectIdReader objectIdReader, Map<String, SettableBeanProperty> props)98 protected AbstractDeserializer(AbstractDeserializer base, 99 ObjectIdReader objectIdReader, Map<String, SettableBeanProperty> props) 100 { 101 _baseType = base._baseType; 102 _backRefProperties = base._backRefProperties; 103 _acceptString = base._acceptString; 104 _acceptBoolean = base._acceptBoolean; 105 _acceptInt = base._acceptInt; 106 _acceptDouble = base._acceptDouble; 107 108 _objectIdReader = objectIdReader; 109 _properties = props; 110 } 111 112 /** 113 * Factory method used when constructing instances for non-POJO types, like 114 * {@link java.util.Map}s. 115 * 116 * @since 2.3 117 */ constructForNonPOJO(BeanDescription beanDesc)118 public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc) { 119 return new AbstractDeserializer(beanDesc); 120 } 121 122 @Override createContextual(DeserializationContext ctxt, BeanProperty property)123 public JsonDeserializer<?> createContextual(DeserializationContext ctxt, 124 BeanProperty property) throws JsonMappingException 125 { 126 final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); 127 if (property != null && intr != null) { 128 final AnnotatedMember accessor = property.getMember(); 129 if (accessor != null) { 130 ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); 131 if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory) 132 JavaType idType; 133 ObjectIdGenerator<?> idGen; 134 SettableBeanProperty idProp = null; 135 ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo); 136 137 // 2.1: allow modifications by "id ref" annotations as well: 138 objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); 139 Class<?> implClass = objectIdInfo.getGeneratorType(); 140 141 if (implClass == ObjectIdGenerators.PropertyGenerator.class) { 142 PropertyName propName = objectIdInfo.getPropertyName(); 143 idProp = (_properties == null) ? null : _properties.get(propName.getSimpleName()); 144 if (idProp == null) { 145 ctxt.reportBadDefinition(_baseType, String.format( 146 "Invalid Object Id definition for %s: cannot find property with name '%s'", 147 handledType().getName(), propName)); 148 } 149 idType = idProp.getType(); 150 idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope()); 151 /* 152 ctxt.reportBadDefinition(_baseType, String.format( 153 / 154 "Invalid Object Id definition for abstract type %s: cannot use `PropertyGenerator` on polymorphic types using property annotation", 155 handledType().getName())); 156 */ 157 } else { // other types simpler 158 resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo); 159 JavaType type = ctxt.constructType(implClass); 160 idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; 161 idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo); 162 } 163 JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType); 164 ObjectIdReader oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(), 165 idGen, deser, idProp, resolver); 166 return new AbstractDeserializer(this, oir, null); 167 } 168 } 169 } 170 if (_properties == null) { 171 return this; 172 } 173 // Need to ensure properties are dropped at this point, regardless 174 return new AbstractDeserializer(this, _objectIdReader, null); 175 } 176 177 /* 178 /********************************************************** 179 /* Public accessors 180 /********************************************************** 181 */ 182 183 @Override handledType()184 public Class<?> handledType() { 185 return _baseType.getRawClass(); 186 } 187 188 @Override isCachable()189 public boolean isCachable() { return true; } 190 191 @Override // since 2.12 logicalType()192 public LogicalType logicalType() { 193 // 30-May-2020, tatu: Not sure if our choice here matters, but let's 194 // guess "POJO" is most likely. If need be, could get more creative 195 return LogicalType.POJO; 196 } 197 198 @Override // since 2.9 supportsUpdate(DeserializationConfig config)199 public Boolean supportsUpdate(DeserializationConfig config) { 200 /* 23-Oct-2016, tatu: Not exactly sure what to do with this; polymorphic 201 * type handling seems bit risky so for now claim it "may or may not be" 202 * possible, which does allow explicit per-type/per-property merging attempts, 203 * but avoids general-configuration merges 204 */ 205 return null; 206 } 207 208 /** 209 * Overridden to return true for those instances that are 210 * handling value for which Object Identity handling is enabled 211 * (either via value type or referring property). 212 */ 213 @Override getObjectIdReader()214 public ObjectIdReader getObjectIdReader() { 215 return _objectIdReader; 216 } 217 218 /** 219 * Method called by <code>BeanDeserializer</code> to resolve back reference 220 * part of managed references. 221 */ 222 @Override findBackReference(String logicalName)223 public SettableBeanProperty findBackReference(String logicalName) { 224 return (_backRefProperties == null) ? null : _backRefProperties.get(logicalName); 225 } 226 227 /* 228 /********************************************************** 229 /* Deserializer implementation 230 /********************************************************** 231 */ 232 233 @Override deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer)234 public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, 235 TypeDeserializer typeDeserializer) 236 throws IOException 237 { 238 // Hmmh. One tricky question; for scalar, is it an Object Id, or "Natural" type? 239 // for now, prefer Object Id: 240 if (_objectIdReader != null) { 241 JsonToken t = p.currentToken(); 242 if (t != null) { 243 // Most commonly, a scalar (int id, uuid String, ...) 244 if (t.isScalarValue()) { 245 return _deserializeFromObjectId(p, ctxt); 246 } 247 // but, with 2.5+, a simple Object-wrapped value also legal: 248 if (t == JsonToken.START_OBJECT) { 249 t = p.nextToken(); 250 } 251 if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject() 252 && _objectIdReader.isValidReferencePropertyName(p.currentName(), p)) { 253 return _deserializeFromObjectId(p, ctxt); 254 } 255 } 256 } 257 // First: support "natural" values (which are always serialized without type info!) 258 Object result = _deserializeIfNatural(p, ctxt); 259 if (result != null) { 260 return result; 261 } 262 return typeDeserializer.deserializeTypedFromObject(p, ctxt); 263 } 264 265 @Override deserialize(JsonParser p, DeserializationContext ctxt)266 public Object deserialize(JsonParser p, DeserializationContext ctxt) 267 throws IOException 268 { 269 // 16-Oct-2016, tatu: Let's pass non-null value instantiator so that we will 270 // get proper exception type; needed to establish there are no creators 271 // (since without ValueInstantiator this would not be known for certain) 272 ValueInstantiator bogus = new ValueInstantiator.Base(_baseType); 273 return ctxt.handleMissingInstantiator(_baseType.getRawClass(), bogus, p, 274 "abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information"); 275 } 276 277 /* 278 /********************************************************** 279 /* Internal methods 280 /********************************************************** 281 */ 282 _deserializeIfNatural(JsonParser p, DeserializationContext ctxt)283 protected Object _deserializeIfNatural(JsonParser p, DeserializationContext ctxt) throws IOException 284 { 285 /* There is a chance we might be "natural" types 286 * (String, Boolean, Integer, Double), which do not include any type information... 287 * Care must be taken to only return this if return type matches, however. 288 * Finally, we may have to consider possibility of custom handlers for 289 * these values: but for now this should work ok. 290 */ 291 switch (p.currentTokenId()) { 292 case JsonTokenId.ID_STRING: 293 if (_acceptString) { 294 return p.getText(); 295 } 296 break; 297 case JsonTokenId.ID_NUMBER_INT: 298 if (_acceptInt) { 299 return p.getIntValue(); 300 } 301 break; 302 case JsonTokenId.ID_NUMBER_FLOAT: 303 if (_acceptDouble) { 304 return Double.valueOf(p.getDoubleValue()); 305 } 306 break; 307 case JsonTokenId.ID_TRUE: 308 if (_acceptBoolean) { 309 return Boolean.TRUE; 310 } 311 break; 312 case JsonTokenId.ID_FALSE: 313 if (_acceptBoolean) { 314 return Boolean.FALSE; 315 } 316 break; 317 } 318 return null; 319 } 320 321 /** 322 * Method called in cases where it looks like we got an Object Id 323 * to parse and use as a reference. 324 */ _deserializeFromObjectId(JsonParser p, DeserializationContext ctxt)325 protected Object _deserializeFromObjectId(JsonParser p, DeserializationContext ctxt) throws IOException 326 { 327 Object id = _objectIdReader.readObjectReference(p, ctxt); 328 ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); 329 // do we have it resolved? 330 Object pojo = roid.resolve(); 331 if (pojo == null) { // not yet; should wait... 332 throw new UnresolvedForwardReference(p, 333 "Could not resolve Object Id ["+id+"] -- unresolved forward-reference?", p.getCurrentLocation(), roid); 334 } 335 return pojo; 336 } 337 } 338