Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 1 | package com.fasterxml.jackson.databind.deser; |
| 2 | |
| 3 | import java.io.IOException; |
Tatu Saloranta | 337feca | 2012-03-19 19:11:21 -0700 | [diff] [blame] | 4 | import java.util.*; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 5 | |
| 6 | import com.fasterxml.jackson.core.*; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 7 | import com.fasterxml.jackson.databind.*; |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 8 | import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; |
| 9 | import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId; |
Tatu Saloranta | df6302f | 2011-12-23 20:05:35 -0800 | [diff] [blame] | 10 | import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 11 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 12 | /** |
| 13 | * Deserializer only used for abstract types used as placeholders during polymorphic |
| 14 | * type handling deserialization. If so, there is no real deserializer associated |
| 15 | * with nominal type, just {@link TypeDeserializer}; and any calls that do not |
| 16 | * pass such resolver will result in an error. |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 17 | */ |
| 18 | public class AbstractDeserializer |
| 19 | extends JsonDeserializer<Object> |
Tatu Saloranta | 1baf61d | 2012-11-05 11:53:35 -0700 | [diff] [blame] | 20 | implements java.io.Serializable |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 21 | { |
Tatu Saloranta | 1baf61d | 2012-11-05 11:53:35 -0700 | [diff] [blame] | 22 | private static final long serialVersionUID = -3010349050434697698L; |
| 23 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 24 | protected final JavaType _baseType; |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 25 | |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 26 | protected final ObjectIdReader _objectIdReader; |
Tatu Saloranta | 337feca | 2012-03-19 19:11:21 -0700 | [diff] [blame] | 27 | |
| 28 | protected final Map<String, SettableBeanProperty> _backRefProperties; |
Tatu Saloranta | a3a8d3c | 2013-08-26 17:46:04 -0700 | [diff] [blame] | 29 | |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 30 | // support for "native" types, which require special care: |
| 31 | |
| 32 | protected final boolean _acceptString; |
| 33 | protected final boolean _acceptBoolean; |
| 34 | protected final boolean _acceptInt; |
| 35 | protected final boolean _acceptDouble; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 36 | |
Tatu Saloranta | 337feca | 2012-03-19 19:11:21 -0700 | [diff] [blame] | 37 | public AbstractDeserializer(BeanDeserializerBuilder builder, |
| 38 | BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps) |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 39 | { |
Tatu Saloranta | 337feca | 2012-03-19 19:11:21 -0700 | [diff] [blame] | 40 | _baseType = beanDesc.getType(); |
| 41 | _objectIdReader = builder.getObjectIdReader(); |
| 42 | _backRefProperties = backRefProps; |
| 43 | Class<?> cls = _baseType.getRawClass(); |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 44 | _acceptString = cls.isAssignableFrom(String.class); |
| 45 | _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class); |
| 46 | _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class); |
| 47 | _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 48 | } |
| 49 | |
Tatu Saloranta | a3a8d3c | 2013-08-26 17:46:04 -0700 | [diff] [blame] | 50 | protected AbstractDeserializer(BeanDescription beanDesc) |
| 51 | { |
| 52 | _baseType = beanDesc.getType(); |
| 53 | _objectIdReader = null; |
| 54 | _backRefProperties = null; |
| 55 | Class<?> cls = _baseType.getRawClass(); |
| 56 | _acceptString = cls.isAssignableFrom(String.class); |
| 57 | _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class); |
| 58 | _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class); |
| 59 | _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class); |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Factory method used when constructing instances for non-POJO types, like |
| 64 | * {@link java.util.Map}s. |
| 65 | * |
| 66 | * @since 2.3 |
| 67 | */ |
| 68 | public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc) |
| 69 | { |
| 70 | return new AbstractDeserializer(beanDesc); |
| 71 | } |
| 72 | |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 73 | /* |
| 74 | /********************************************************** |
| 75 | /* Public accessors |
| 76 | /********************************************************** |
| 77 | */ |
| 78 | |
| 79 | @Override |
Tatu Saloranta | 47ac6bd | 2013-09-05 20:55:00 -0700 | [diff] [blame^] | 80 | public Class<?> handledType() { |
| 81 | return _baseType.getRawClass(); |
| 82 | } |
| 83 | |
| 84 | @Override |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 85 | public boolean isCachable() { return true; } |
| 86 | |
| 87 | /** |
| 88 | * Overridden to return true for those instances that are |
| 89 | * handling value for which Object Identity handling is enabled |
| 90 | * (either via value type or referring property). |
| 91 | */ |
| 92 | @Override |
| 93 | public ObjectIdReader getObjectIdReader() { |
| 94 | return _objectIdReader; |
| 95 | } |
| 96 | |
Tatu Saloranta | 337feca | 2012-03-19 19:11:21 -0700 | [diff] [blame] | 97 | /** |
| 98 | * Method called by <code>BeanDeserializer</code> to resolve back reference |
| 99 | * part of managed references. |
| 100 | */ |
Tatu Saloranta | c6eaf4b | 2013-07-04 20:25:17 -0700 | [diff] [blame] | 101 | @Override |
| 102 | public SettableBeanProperty findBackReference(String logicalName) { |
Tatu Saloranta | 337feca | 2012-03-19 19:11:21 -0700 | [diff] [blame] | 103 | return (_backRefProperties == null) ? null : _backRefProperties.get(logicalName); |
| 104 | } |
| 105 | |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 106 | /* |
| 107 | /********************************************************** |
| 108 | /* Deserializer implementation |
| 109 | /********************************************************** |
| 110 | */ |
| 111 | |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 112 | @Override |
| 113 | public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, |
| 114 | TypeDeserializer typeDeserializer) |
| 115 | throws IOException, JsonProcessingException |
| 116 | { |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 117 | // Hmmh. One tricky question; for scalar, is it an Object Id, or "Natural" type? |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 118 | // for now, prefer Object Id: |
| 119 | if (_objectIdReader != null) { |
| 120 | JsonToken t = jp.getCurrentToken(); |
| 121 | // should be good enough check; we only care about Strings, integral numbers: |
| 122 | if (t != null && t.isScalarValue()) { |
| 123 | return _deserializeFromObjectId(jp, ctxt); |
| 124 | } |
| 125 | } |
| 126 | |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 127 | // First: support "natural" values (which are always serialized without type info!) |
| 128 | Object result = _deserializeIfNatural(jp, ctxt); |
| 129 | if (result != null) { |
| 130 | return result; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 131 | } |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 132 | return typeDeserializer.deserializeTypedFromObject(jp, ctxt); |
| 133 | } |
| 134 | |
| 135 | @Override |
| 136 | public Object deserialize(JsonParser jp, DeserializationContext ctxt) |
| 137 | throws IOException, JsonProcessingException |
| 138 | { |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 139 | // This method should never be called... |
Tatu | f0929ac | 2012-01-25 16:24:40 -0800 | [diff] [blame] | 140 | throw ctxt.instantiationException(_baseType.getRawClass(), |
| 141 | "abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information"); |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 142 | } |
| 143 | |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 144 | /* |
| 145 | /********************************************************** |
| 146 | /* Internal methods |
| 147 | /********************************************************** |
| 148 | */ |
| 149 | |
Tatu Saloranta | 81c9937 | 2013-02-17 19:05:42 -0800 | [diff] [blame] | 150 | @SuppressWarnings("incomplete-switch") |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 151 | protected Object _deserializeIfNatural(JsonParser jp, DeserializationContext ctxt) |
| 152 | throws IOException, JsonProcessingException |
| 153 | { |
Tatu | f0929ac | 2012-01-25 16:24:40 -0800 | [diff] [blame] | 154 | /* As per [JACKSON-417], there is a chance we might be "natural" types |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 155 | * (String, Boolean, Integer, Double), which do not include any type information... |
| 156 | * Care must be taken to only return this if return type matches, however. |
| 157 | * Finally, we may have to consider possibility of custom handlers for |
| 158 | * these values: but for now this should work ok. |
| 159 | */ |
| 160 | switch (jp.getCurrentToken()) { |
| 161 | case VALUE_STRING: |
| 162 | if (_acceptString) { |
| 163 | return jp.getText(); |
| 164 | } |
| 165 | break; |
| 166 | case VALUE_NUMBER_INT: |
| 167 | if (_acceptInt) { |
| 168 | return jp.getIntValue(); |
| 169 | } |
| 170 | break; |
| 171 | |
| 172 | case VALUE_NUMBER_FLOAT: |
| 173 | if (_acceptDouble) { |
| 174 | return Double.valueOf(jp.getDoubleValue()); |
| 175 | } |
| 176 | break; |
| 177 | case VALUE_TRUE: |
| 178 | if (_acceptBoolean) { |
| 179 | return Boolean.TRUE; |
| 180 | } |
| 181 | break; |
| 182 | case VALUE_FALSE: |
| 183 | if (_acceptBoolean) { |
| 184 | return Boolean.FALSE; |
| 185 | } |
| 186 | break; |
| 187 | } |
| 188 | return null; |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 189 | } |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 190 | |
| 191 | /** |
| 192 | * Method called in cases where it looks like we got an Object Id |
| 193 | * to parse and use as a reference. |
| 194 | */ |
| 195 | protected Object _deserializeFromObjectId(JsonParser jp, DeserializationContext ctxt) |
| 196 | throws IOException, JsonProcessingException |
| 197 | { |
Tatu Saloranta | 9e64906 | 2013-08-05 20:29:33 -0700 | [diff] [blame] | 198 | Object id = _objectIdReader.readObjectReference(jp, ctxt); |
Tatu Saloranta | 2481fa4 | 2012-03-19 18:09:00 -0700 | [diff] [blame] | 199 | ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator); |
| 200 | // do we have it resolved? |
| 201 | Object pojo = roid.item; |
| 202 | if (pojo == null) { // not yet; should wait... |
| 203 | throw new IllegalStateException("Could not resolve Object Id ["+id+"] -- unresolved forward-reference?"); |
| 204 | } |
| 205 | return pojo; |
| 206 | } |
Tatu Saloranta | e4f23bb | 2011-12-23 00:31:35 -0800 | [diff] [blame] | 207 | } |
Tatu Saloranta | ba0470f | 2011-12-29 22:14:05 -0800 | [diff] [blame] | 208 | |