Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 1 | package com.fasterxml.jackson.databind.jsontype.impl; |
| 2 | |
| 3 | import java.io.IOException; |
| 4 | import java.util.HashMap; |
| 5 | |
Tatu Saloranta | 1b253d3 | 2011-12-23 00:44:25 -0800 | [diff] [blame] | 6 | import com.fasterxml.jackson.annotation.JsonTypeInfo; |
| 7 | import com.fasterxml.jackson.core.*; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 8 | import com.fasterxml.jackson.databind.BeanProperty; |
| 9 | import com.fasterxml.jackson.databind.DeserializationContext; |
Tatu Saloranta | 192c1dc | 2013-02-06 22:28:58 -0800 | [diff] [blame] | 10 | import com.fasterxml.jackson.databind.DeserializationFeature; |
Tatu Saloranta | d92c1ed | 2011-12-23 18:23:31 -0800 | [diff] [blame] | 11 | import com.fasterxml.jackson.databind.JavaType; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 12 | import com.fasterxml.jackson.databind.JsonDeserializer; |
Tatu Saloranta | 192c1dc | 2013-02-06 22:28:58 -0800 | [diff] [blame] | 13 | import com.fasterxml.jackson.databind.deser.std.NullifyingDeserializer; |
Tatu Saloranta | df6302f | 2011-12-23 20:05:35 -0800 | [diff] [blame] | 14 | import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 15 | import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; |
Tatu Saloranta | 46c6679 | 2014-05-19 23:42:47 -0700 | [diff] [blame^] | 16 | import com.fasterxml.jackson.databind.util.ClassUtil; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 17 | |
| 18 | /** |
Tatu Saloranta | b2a9ca7 | 2012-01-20 20:16:21 -0800 | [diff] [blame] | 19 | * Base class for all standard Jackson {@link TypeDeserializer}s. |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 20 | */ |
Tatu Saloranta | 65d186e | 2012-10-05 23:51:38 -0700 | [diff] [blame] | 21 | public abstract class TypeDeserializerBase |
| 22 | extends TypeDeserializer |
| 23 | implements java.io.Serializable |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 24 | { |
Tatu Saloranta | 65d186e | 2012-10-05 23:51:38 -0700 | [diff] [blame] | 25 | private static final long serialVersionUID = 278445030337366675L; |
Tatu Saloranta | 192c1dc | 2013-02-06 22:28:58 -0800 | [diff] [blame] | 26 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 27 | protected final TypeIdResolver _idResolver; |
| 28 | |
| 29 | protected final JavaType _baseType; |
| 30 | |
Tatu Saloranta | 49b7121 | 2012-01-30 22:13:21 -0800 | [diff] [blame] | 31 | /** |
| 32 | * Property that contains value for which type information |
| 33 | * is included; null if value is a root value. |
| 34 | * Note that this value is not assigned during construction |
| 35 | * but only when {@link #forProperty} is called to create |
| 36 | * a copy. |
| 37 | */ |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 38 | protected final BeanProperty _property; |
| 39 | |
| 40 | /** |
| 41 | * Type to use as the default implementation, if type id is |
| 42 | * missing or can not be resolved. |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 43 | */ |
| 44 | protected final JavaType _defaultImpl; |
Tatu Saloranta | 8958048 | 2012-01-20 22:08:56 -0800 | [diff] [blame] | 45 | |
| 46 | /** |
| 47 | * Name of type property used; needed for non-property versions too, |
| 48 | * in cases where type id is to be exposed as part of JSON. |
| 49 | */ |
| 50 | protected final String _typePropertyName; |
| 51 | |
| 52 | protected final boolean _typeIdVisible; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 53 | |
| 54 | /** |
| 55 | * For efficient operation we will lazily build mappings from type ids |
| 56 | * to actual deserializers, once needed. |
| 57 | */ |
| 58 | protected final HashMap<String,JsonDeserializer<Object>> _deserializers; |
| 59 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 60 | protected JsonDeserializer<Object> _defaultImplDeserializer; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 61 | |
Tatu Saloranta | 49b7121 | 2012-01-30 22:13:21 -0800 | [diff] [blame] | 62 | /* |
| 63 | /********************************************************** |
| 64 | /* Life-cycle |
| 65 | /********************************************************** |
| 66 | */ |
| 67 | |
| 68 | protected TypeDeserializerBase(JavaType baseType, TypeIdResolver idRes, |
Tatu Saloranta | 8958048 | 2012-01-20 22:08:56 -0800 | [diff] [blame] | 69 | String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl) |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 70 | { |
| 71 | _baseType = baseType; |
| 72 | _idResolver = idRes; |
Tatu Saloranta | 8958048 | 2012-01-20 22:08:56 -0800 | [diff] [blame] | 73 | _typePropertyName = typePropertyName; |
| 74 | _typeIdVisible = typeIdVisible; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 75 | _deserializers = new HashMap<String,JsonDeserializer<Object>>(); |
| 76 | if (defaultImpl == null) { |
| 77 | _defaultImpl = null; |
| 78 | } else { |
| 79 | /* 16-Oct-2011, tatu: should call this via TypeFactory; this is |
| 80 | * not entirely safe... however, since Collections/Maps are |
| 81 | * seldom (if ever) base types, may be ok. |
| 82 | */ |
| 83 | _defaultImpl = baseType.forcedNarrowBy(defaultImpl); |
| 84 | } |
Tatu Saloranta | 49b7121 | 2012-01-30 22:13:21 -0800 | [diff] [blame] | 85 | |
| 86 | _property = null; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 87 | } |
| 88 | |
Tatu Saloranta | 49b7121 | 2012-01-30 22:13:21 -0800 | [diff] [blame] | 89 | protected TypeDeserializerBase(TypeDeserializerBase src, BeanProperty property) |
| 90 | { |
| 91 | _baseType = src._baseType; |
| 92 | _idResolver = src._idResolver; |
| 93 | _typePropertyName = src._typePropertyName; |
| 94 | _typeIdVisible = src._typeIdVisible; |
| 95 | _deserializers = src._deserializers; |
| 96 | _defaultImpl = src._defaultImpl; |
| 97 | _defaultImplDeserializer = src._defaultImplDeserializer; |
| 98 | |
| 99 | _property = property; |
| 100 | } |
| 101 | |
| 102 | @Override |
| 103 | public abstract TypeDeserializer forProperty(BeanProperty prop); |
| 104 | |
| 105 | /* |
| 106 | /********************************************************** |
| 107 | /* Accessors |
| 108 | /********************************************************** |
| 109 | */ |
| 110 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 111 | @Override |
| 112 | public abstract JsonTypeInfo.As getTypeInclusion(); |
| 113 | |
| 114 | public String baseTypeName() { return _baseType.getRawClass().getName(); } |
| 115 | |
| 116 | @Override |
Tatu Saloranta | 8958048 | 2012-01-20 22:08:56 -0800 | [diff] [blame] | 117 | public final String getPropertyName() { return _typePropertyName; } |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 118 | |
| 119 | @Override |
| 120 | public TypeIdResolver getTypeIdResolver() { return _idResolver; } |
| 121 | |
| 122 | @Override |
| 123 | public Class<?> getDefaultImpl() { |
| 124 | return (_defaultImpl == null) ? null : _defaultImpl.getRawClass(); |
| 125 | } |
| 126 | |
| 127 | @Override |
| 128 | public String toString() |
| 129 | { |
Tatu Saloranta | a795fa2 | 2013-01-18 20:05:11 -0800 | [diff] [blame] | 130 | StringBuilder sb = new StringBuilder(); |
| 131 | sb.append('[').append(getClass().getName()); |
| 132 | sb.append("; base-type:").append(_baseType); |
| 133 | sb.append("; id-resolver: ").append(_idResolver); |
| 134 | sb.append(']'); |
| 135 | return sb.toString(); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | /* |
| 139 | /********************************************************** |
| 140 | /* Helper methods for sub-classes |
| 141 | /********************************************************** |
| 142 | */ |
| 143 | |
Tatu Saloranta | b6da866 | 2014-05-04 18:49:20 -0700 | [diff] [blame] | 144 | protected final JsonDeserializer<Object> _findDeserializer(DeserializationContext ctxt, String typeId) |
| 145 | throws IOException |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 146 | { |
| 147 | JsonDeserializer<Object> deser; |
| 148 | |
| 149 | synchronized (_deserializers) { |
| 150 | deser = _deserializers.get(typeId); |
| 151 | if (deser == null) { |
Tatu Saloranta | ee67dee | 2013-09-28 17:11:46 -0700 | [diff] [blame] | 152 | /* As per [Issue#305], need to provide contextual info. But for |
| 153 | * backwards compatibility, let's start by only supporting this |
| 154 | * for base class, not via interface. Later on we can add this |
| 155 | * to the interface, assuming deprecation at base class helps. |
| 156 | */ |
| 157 | JavaType type; |
| 158 | if (_idResolver instanceof TypeIdResolverBase) { |
| 159 | type = ((TypeIdResolverBase) _idResolver).typeFromId(ctxt, typeId); |
| 160 | } else { |
| 161 | type = _idResolver.typeFromId(typeId); |
| 162 | } |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 163 | if (type == null) { |
| 164 | // As per [JACKSON-614], use the default impl if no type id available: |
| 165 | if (_defaultImpl == null) { |
| 166 | throw ctxt.unknownTypeException(_baseType, typeId); |
| 167 | } |
| 168 | deser = _findDefaultImplDeserializer(ctxt); |
| 169 | } else { |
| 170 | /* 16-Dec-2010, tatu: Since nominal type we get here has no (generic) type parameters, |
| 171 | * we actually now need to explicitly narrow from base type (which may have parameterization) |
| 172 | * using raw type. |
| 173 | * |
| 174 | * One complication, though; can not change 'type class' (simple type to container); otherwise |
| 175 | * we may try to narrow a SimpleType (Object.class) into MapType (Map.class), losing actual |
| 176 | * type in process (getting SimpleType of Map.class which will not work as expected) |
| 177 | */ |
| 178 | if (_baseType != null && _baseType.getClass() == type.getClass()) { |
| 179 | type = _baseType.narrowBy(type.getRawClass()); |
| 180 | } |
Tatu | c3a73d0 | 2012-01-31 12:45:49 -0800 | [diff] [blame] | 181 | deser = ctxt.findContextualValueDeserializer(type, _property); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 182 | } |
| 183 | _deserializers.put(typeId, deser); |
| 184 | } |
| 185 | } |
| 186 | return deser; |
| 187 | } |
| 188 | |
Tatu Saloranta | b6da866 | 2014-05-04 18:49:20 -0700 | [diff] [blame] | 189 | protected final JsonDeserializer<Object> _findDefaultImplDeserializer(DeserializationContext ctxt) throws IOException |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 190 | { |
Tatu Saloranta | 192c1dc | 2013-02-06 22:28:58 -0800 | [diff] [blame] | 191 | /* 06-Feb-2013, tatu: As per [Issue#148], consider default implementation value of |
| 192 | * {@link NoClass} to mean "serialize as null"; as well as DeserializationFeature |
| 193 | * to do swift mapping to null |
| 194 | */ |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 195 | if (_defaultImpl == null) { |
Tatu Saloranta | 192c1dc | 2013-02-06 22:28:58 -0800 | [diff] [blame] | 196 | if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) { |
| 197 | return NullifyingDeserializer.instance; |
| 198 | } |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 199 | return null; |
| 200 | } |
Tatu Saloranta | 46c6679 | 2014-05-19 23:42:47 -0700 | [diff] [blame^] | 201 | Class<?> raw = _defaultImpl.getRawClass(); |
| 202 | if (ClassUtil.isBogusClass(raw)) { |
Tatu Saloranta | 192c1dc | 2013-02-06 22:28:58 -0800 | [diff] [blame] | 203 | return NullifyingDeserializer.instance; |
| 204 | } |
| 205 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 206 | synchronized (_defaultImpl) { |
| 207 | if (_defaultImplDeserializer == null) { |
Tatu | c3a73d0 | 2012-01-31 12:45:49 -0800 | [diff] [blame] | 208 | _defaultImplDeserializer = ctxt.findContextualValueDeserializer( |
| 209 | _defaultImpl, _property); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 210 | } |
| 211 | return _defaultImplDeserializer; |
| 212 | } |
| 213 | } |
Tatu Saloranta | 8b5fd60 | 2013-08-03 13:41:28 -0700 | [diff] [blame] | 214 | |
| 215 | /** |
| 216 | * Helper method called when {@link JsonParser} indicates that it can use |
| 217 | * so-called native type ids. Assumption from there is that only native |
| 218 | * type ids are to be used. |
| 219 | * |
| 220 | * @since 2.3 |
| 221 | */ |
Tatu Saloranta | b6da866 | 2014-05-04 18:49:20 -0700 | [diff] [blame] | 222 | @Deprecated |
| 223 | protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt) throws IOException { |
| 224 | return _deserializeWithNativeTypeId(jp, ctxt, jp.getTypeId()); |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * Helper method called when {@link JsonParser} indicates that it can use |
| 229 | * so-called native type ids, and such type id has been found. |
| 230 | * |
| 231 | * @since 2.4 |
| 232 | */ |
| 233 | protected Object _deserializeWithNativeTypeId(JsonParser jp, DeserializationContext ctxt, Object typeId) |
Tatu Saloranta | 5bdafdc | 2014-05-04 19:52:56 -0700 | [diff] [blame] | 234 | throws IOException |
Tatu Saloranta | 8b5fd60 | 2013-08-03 13:41:28 -0700 | [diff] [blame] | 235 | { |
Tatu Saloranta | 8b5fd60 | 2013-08-03 13:41:28 -0700 | [diff] [blame] | 236 | JsonDeserializer<Object> deser; |
Tatu Saloranta | b6da866 | 2014-05-04 18:49:20 -0700 | [diff] [blame] | 237 | if (typeId == null) { |
| 238 | /* 04-May-2014, tatu: Should error be obligatory, or should there be another method |
| 239 | * for "try to deserialize with native tpye id"? |
| 240 | */ |
| 241 | if (_defaultImpl == null) { |
Tatu Saloranta | 8b5fd60 | 2013-08-03 13:41:28 -0700 | [diff] [blame] | 242 | throw ctxt.mappingException("No (native) type id found when one was expected for polymorphic type handling"); |
| 243 | } |
Tatu Saloranta | b6da866 | 2014-05-04 18:49:20 -0700 | [diff] [blame] | 244 | deser = _findDefaultImplDeserializer(ctxt); |
Tatu Saloranta | 8b5fd60 | 2013-08-03 13:41:28 -0700 | [diff] [blame] | 245 | } else { |
Tatu Saloranta | b6da866 | 2014-05-04 18:49:20 -0700 | [diff] [blame] | 246 | String typeIdStr = (typeId instanceof String) ? (String) typeId : String.valueOf(typeId); |
| 247 | deser = _findDeserializer(ctxt, typeIdStr); |
Tatu Saloranta | 8b5fd60 | 2013-08-03 13:41:28 -0700 | [diff] [blame] | 248 | } |
Tatu Saloranta | 8b5fd60 | 2013-08-03 13:41:28 -0700 | [diff] [blame] | 249 | return deser.deserialize(jp, ctxt); |
| 250 | } |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 251 | } |