aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
blob: 578afd940b84d948ebc8651498b27b07bd37a93c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
package com.fasterxml.jackson.databind.deser;

import java.io.IOException;
import java.util.*;

import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.annotation.ObjectIdResolver;

import com.fasterxml.jackson.core.*;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
import com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.LogicalType;

/**
 * Deserializer only used for abstract types used as placeholders during polymorphic
 * type handling deserialization. If so, there is no real deserializer associated
 * with nominal type, just {@link TypeDeserializer}; and any calls that do not
 * pass such resolver will result in an error.
 */
public class AbstractDeserializer
    extends JsonDeserializer<Object>
    implements ContextualDeserializer, // since 2.9
        java.io.Serializable
{
    private static final long serialVersionUID = 1L;

    protected final JavaType _baseType;

    protected final ObjectIdReader _objectIdReader;

    protected final Map<String, SettableBeanProperty> _backRefProperties;

    protected transient Map<String,SettableBeanProperty> _properties;

    // support for "native" types, which require special care:
    
    protected final boolean _acceptString;
    protected final boolean _acceptBoolean;
    protected final boolean _acceptInt;
    protected final boolean _acceptDouble;

    /*
    /**********************************************************
    /* Life cycle
    /**********************************************************
     */

    /**
     * @since 2.9
     *
     * @param props Regular properties: currently only needed to support property-annotated
     *    Object Id handling with property inclusion (needed for determining type of Object Id
     *    to bind)
     */
    public AbstractDeserializer(BeanDeserializerBuilder builder,
            BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps,
            Map<String, SettableBeanProperty> props)
    {
        _baseType = beanDesc.getType();
        _objectIdReader = builder.getObjectIdReader();
        _backRefProperties = backRefProps;
        _properties = props;
        Class<?> cls = _baseType.getRawClass();
        _acceptString = cls.isAssignableFrom(String.class);
        _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class);
        _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class);
        _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class);
    }

    @Deprecated // since 2.9
    public AbstractDeserializer(BeanDeserializerBuilder builder,
            BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps) {
        this(builder, beanDesc, backRefProps, null);
    }

    protected AbstractDeserializer(BeanDescription beanDesc)
    {
        _baseType = beanDesc.getType();
        _objectIdReader = null;
        _backRefProperties = null;
        Class<?> cls = _baseType.getRawClass();
        _acceptString = cls.isAssignableFrom(String.class);
        _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class);
        _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class);
        _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class);
    }

    /**
     * @since 2.9
     */
    protected AbstractDeserializer(AbstractDeserializer base,
            ObjectIdReader objectIdReader, Map<String, SettableBeanProperty> props)
    {
        _baseType = base._baseType;
        _backRefProperties = base._backRefProperties;
        _acceptString = base._acceptString;
        _acceptBoolean = base._acceptBoolean;
        _acceptInt = base._acceptInt;
        _acceptDouble = base._acceptDouble;

        _objectIdReader = objectIdReader;
        _properties = props;
    }

    /**
     * Factory method used when constructing instances for non-POJO types, like
     * {@link java.util.Map}s.
     * 
     * @since 2.3
     */
    public static AbstractDeserializer constructForNonPOJO(BeanDescription beanDesc) {
        return new AbstractDeserializer(beanDesc);
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
            BeanProperty property) throws JsonMappingException
    {
        final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
        if (property != null && intr != null) {
            final AnnotatedMember accessor = property.getMember();
            if (accessor != null) {
                ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
                if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory)
                    JavaType idType;
                    ObjectIdGenerator<?> idGen;
                    SettableBeanProperty idProp = null;
                    ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);

                    // 2.1: allow modifications by "id ref" annotations as well:
                    objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo);
                    Class<?> implClass = objectIdInfo.getGeneratorType();

                    if (implClass == ObjectIdGenerators.PropertyGenerator.class) {
                        PropertyName propName = objectIdInfo.getPropertyName();
                        idProp = (_properties == null) ? null : _properties.get(propName.getSimpleName());
                        if (idProp == null) {
                            ctxt.reportBadDefinition(_baseType, String.format(
                                    "Invalid Object Id definition for %s: cannot find property with name '%s'",
                                    handledType().getName(), propName));
                        }
                        idType = idProp.getType();
                        idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
/*
                         ctxt.reportBadDefinition(_baseType, String.format(
/
"Invalid Object Id definition for abstract type %s: cannot use `PropertyGenerator` on polymorphic types using property annotation",
handledType().getName()));
*/
                    } else { // other types simpler
                        resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
                        JavaType type = ctxt.constructType(implClass);
                        idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
                        idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo);
                    }
                    JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
                    ObjectIdReader oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(),
                             idGen, deser, idProp, resolver);
                    return new AbstractDeserializer(this, oir, null);
                }
            }
        }
        if (_properties == null) {
            return this;
        }
        // Need to ensure properties are dropped at this point, regardless
        return new AbstractDeserializer(this, _objectIdReader, null);
    }

    /*
    /**********************************************************
    /* Public accessors
    /**********************************************************
     */

    @Override
    public Class<?> handledType() {
        return _baseType.getRawClass();
    }
    
    @Override
    public boolean isCachable() { return true; }

    @Override // since 2.12
    public LogicalType logicalType() {
        // 30-May-2020, tatu: Not sure if our choice here matters, but let's
        //     guess "POJO" is most likely. If need be, could get more creative
        return LogicalType.POJO;
    }

    @Override // since 2.9
    public Boolean supportsUpdate(DeserializationConfig config) {
        /* 23-Oct-2016, tatu: Not exactly sure what to do with this; polymorphic
         *   type handling seems bit risky so for now claim it "may or may not be"
         *   possible, which does allow explicit per-type/per-property merging attempts,
         *   but avoids general-configuration merges
         */
        return null;
    }

    /**
     * Overridden to return true for those instances that are
     * handling value for which Object Identity handling is enabled
     * (either via value type or referring property).
     */
    @Override
    public ObjectIdReader getObjectIdReader() {
        return _objectIdReader;
    }

    /**
     * Method called by <code>BeanDeserializer</code> to resolve back reference
     * part of managed references.
     */
    @Override
    public SettableBeanProperty findBackReference(String logicalName) {
        return (_backRefProperties == null) ? null : _backRefProperties.get(logicalName);
    }
    
    /*
    /**********************************************************
    /* Deserializer implementation
    /**********************************************************
     */

    @Override
    public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
            TypeDeserializer typeDeserializer)
        throws IOException
    {
        // Hmmh. One tricky question; for scalar, is it an Object Id, or "Natural" type?
        // for now, prefer Object Id:
        if (_objectIdReader != null) {
            JsonToken t = p.currentToken();
            if (t != null) {
                // Most commonly, a scalar (int id, uuid String, ...)
                if (t.isScalarValue()) {
                    return _deserializeFromObjectId(p, ctxt);
                }
                // but, with 2.5+, a simple Object-wrapped value also legal:
                if (t == JsonToken.START_OBJECT) {
                    t = p.nextToken();
                }
                if ((t == JsonToken.FIELD_NAME) && _objectIdReader.maySerializeAsObject()
                        && _objectIdReader.isValidReferencePropertyName(p.currentName(), p)) {
                    return _deserializeFromObjectId(p, ctxt);
                }
            }
        }
        // First: support "natural" values (which are always serialized without type info!)
        Object result = _deserializeIfNatural(p, ctxt);
        if (result != null) {
            return result;
        }
        return typeDeserializer.deserializeTypedFromObject(p, ctxt);
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt)
        throws IOException
    {
        // 16-Oct-2016, tatu: Let's pass non-null value instantiator so that we will
        //    get proper exception type; needed to establish there are no creators
        //    (since without ValueInstantiator this would not be known for certain)
        ValueInstantiator bogus = new ValueInstantiator.Base(_baseType);
        return ctxt.handleMissingInstantiator(_baseType.getRawClass(), bogus, p,
                "abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information");
    }

    /*
    /**********************************************************
    /* Internal methods
    /**********************************************************
     */

    protected Object _deserializeIfNatural(JsonParser p, DeserializationContext ctxt) throws IOException
    {
        /* There is a chance we might be "natural" types
         * (String, Boolean, Integer, Double), which do not include any type information...
         * Care must be taken to only return this if return type matches, however.
         * Finally, we may have to consider possibility of custom handlers for
         * these values: but for now this should work ok.
         */
        switch (p.currentTokenId()) {
        case JsonTokenId.ID_STRING:
            if (_acceptString) {
                return p.getText();
            }
            break;
        case JsonTokenId.ID_NUMBER_INT:
            if (_acceptInt) {
                return p.getIntValue();
            }
            break;
        case JsonTokenId.ID_NUMBER_FLOAT:
            if (_acceptDouble) {
                return Double.valueOf(p.getDoubleValue());
            }
            break;
        case JsonTokenId.ID_TRUE:
            if (_acceptBoolean) {
                return Boolean.TRUE;
            }
            break;
        case JsonTokenId.ID_FALSE:
            if (_acceptBoolean) {
                return Boolean.FALSE;
            }
            break;
        }
        return null;
    }

    /**
     * Method called in cases where it looks like we got an Object Id
     * to parse and use as a reference.
     */
    protected Object _deserializeFromObjectId(JsonParser p, DeserializationContext ctxt) throws IOException
    {
        Object id = _objectIdReader.readObjectReference(p, ctxt);
        ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
        // do we have it resolved?
        Object pojo = roid.resolve();
        if (pojo == null) { // not yet; should wait...
            throw new UnresolvedForwardReference(p,
                    "Could not resolve Object Id ["+id+"] -- unresolved forward-reference?", p.getCurrentLocation(), roid);
        }
        return pojo;
    }
}