• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.deser;
2 
3 import java.io.IOException;
4 import java.util.Map;
5 
6 import com.fasterxml.jackson.core.*;
7 import com.fasterxml.jackson.databind.*;
8 import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
9 import com.fasterxml.jackson.databind.introspect.AnnotatedField;
10 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
11 import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
12 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
13 import com.fasterxml.jackson.databind.util.ClassUtil;
14 
15 /**
16  * Class that represents a "wildcard" set method which can be used
17  * to generically set values of otherwise unmapped (aka "unknown")
18  * properties read from Json content.
19  *<p>
20  * !!! Note: might make sense to refactor to share some code
21  * with {@link SettableBeanProperty}?
22  */
23 public class SettableAnyProperty
24     implements java.io.Serializable
25 {
26     private static final long serialVersionUID = 1L;
27 
28     /**
29      * Method used for setting "any" properties, along with annotation
30      * information. Retained to allow contextualization of any properties.
31      */
32     protected final BeanProperty _property;
33 
34     /**
35      * Annotated variant is needed for JDK serialization only
36      */
37     final protected AnnotatedMember _setter;
38 
39     final boolean _setterIsField;
40 
41     protected final JavaType _type;
42 
43     protected JsonDeserializer<Object> _valueDeserializer;
44 
45     protected final TypeDeserializer _valueTypeDeserializer;
46 
47     /**
48      * @since 2.9
49      */
50     protected final KeyDeserializer _keyDeserializer;
51 
52     /*
53     /**********************************************************
54     /* Life-cycle
55     /**********************************************************
56      */
57 
SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaType type, KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)58     public SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaType type,
59             KeyDeserializer keyDeser,
60             JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)
61     {
62         _property = property;
63         _setter = setter;
64         _type = type;
65         _valueDeserializer = valueDeser;
66         _valueTypeDeserializer = typeDeser;
67         _keyDeserializer = keyDeser;
68         _setterIsField = setter instanceof AnnotatedField;
69     }
70 
71     @Deprecated // since 2.9
SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaType type, JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)72     public SettableAnyProperty(BeanProperty property, AnnotatedMember setter, JavaType type,
73             JsonDeserializer<Object> valueDeser, TypeDeserializer typeDeser)
74     {
75         this(property, setter, type, null, valueDeser, typeDeser);
76     }
77 
withValueDeserializer(JsonDeserializer<Object> deser)78     public SettableAnyProperty withValueDeserializer(JsonDeserializer<Object> deser) {
79         return new SettableAnyProperty(_property, _setter, _type,
80                 _keyDeserializer, deser, _valueTypeDeserializer);
81     }
82 
fixAccess(DeserializationConfig config)83     public void fixAccess(DeserializationConfig config) {
84         _setter.fixAccess(
85                 config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
86     }
87 
88     /*
89     /**********************************************************
90     /* JDK serialization handling
91     /**********************************************************
92      */
93 
94     /**
95      * Need to define this to verify that we retain actual Method reference
96      */
readResolve()97     Object readResolve() {
98         // sanity check...
99         if (_setter == null || _setter.getAnnotated() == null) {
100             throw new IllegalArgumentException("Missing method (broken JDK (de)serialization?)");
101         }
102         return this;
103     }
104 
105     /*
106     /**********************************************************
107     /* Public API, accessors
108     /**********************************************************
109      */
110 
getProperty()111     public BeanProperty getProperty() { return _property; }
112 
hasValueDeserializer()113     public boolean hasValueDeserializer() { return (_valueDeserializer != null); }
114 
getType()115     public JavaType getType() { return _type; }
116 
117     /*
118     /**********************************************************
119     /* Public API, deserialization
120     /**********************************************************
121      */
122 
123     /**
124      * Method called to deserialize appropriate value, given parser (and
125      * context), and set it using appropriate method (a setter method).
126      */
deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance, String propName)127     public final void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
128             Object instance, String propName)
129         throws IOException
130     {
131         try {
132             Object key = (_keyDeserializer == null) ? propName
133                     : _keyDeserializer.deserializeKey(propName, ctxt);
134             set(instance, key, deserialize(p, ctxt));
135         } catch (UnresolvedForwardReference reference) {
136             if (!(_valueDeserializer.getObjectIdReader() != null)) {
137                 throw JsonMappingException.from(p, "Unresolved forward reference but no identity info.", reference);
138             }
139             AnySetterReferring referring = new AnySetterReferring(this, reference,
140                     _type.getRawClass(), instance, propName);
141             reference.getRoid().appendReferring(referring);
142         }
143     }
144 
deserialize(JsonParser p, DeserializationContext ctxt)145     public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
146     {
147         if (p.hasToken(JsonToken.VALUE_NULL)) {
148             return _valueDeserializer.getNullValue(ctxt);
149         }
150         if (_valueTypeDeserializer != null) {
151             return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
152         }
153         return _valueDeserializer.deserialize(p, ctxt);
154     }
155 
156     @SuppressWarnings("unchecked")
set(Object instance, Object propName, Object value)157     public void set(Object instance, Object propName, Object value) throws IOException
158     {
159         try {
160             // if annotation in the field (only map is supported now)
161             if (_setterIsField) {
162                 AnnotatedField field = (AnnotatedField) _setter;
163                 Map<Object,Object> val = (Map<Object,Object>) field.getValue(instance);
164                 /* 01-Jun-2016, tatu: At this point it is not quite clear what to do if
165                  *    field is `null` -- we cannot necessarily count on zero-args
166                  *    constructor except for a small set of types, so for now just
167                  *    ignore if null. May need to figure out something better in future.
168                  */
169                 if (val != null) {
170                     // add the property key and value
171                     val.put(propName, value);
172                 }
173             } else {
174                 // note: cannot use 'setValue()' due to taking 2 args
175                 ((AnnotatedMethod) _setter).callOnWith(instance, propName, value);
176             }
177         } catch (Exception e) {
178             _throwAsIOE(e, propName, value);
179         }
180     }
181 
182     /*
183     /**********************************************************
184     /* Helper methods
185     /**********************************************************
186      */
187 
188     /**
189      * @param e Exception to re-throw or wrap
190      * @param propName Name of property (from Json input) to set
191      * @param value Value of the property
192      */
_throwAsIOE(Exception e, Object propName, Object value)193     protected void _throwAsIOE(Exception e, Object propName, Object value)
194         throws IOException
195     {
196         if (e instanceof IllegalArgumentException) {
197             String actType = ClassUtil.classNameOf(value);
198             StringBuilder msg = new StringBuilder("Problem deserializing \"any\" property '").append(propName);
199             msg.append("' of class "+getClassName()+" (expected type: ").append(_type);
200             msg.append("; actual type: ").append(actType).append(")");
201             String origMsg = ClassUtil.exceptionMessage(e);
202             if (origMsg != null) {
203                 msg.append(", problem: ").append(origMsg);
204             } else {
205                 msg.append(" (no error message provided)");
206             }
207             throw new JsonMappingException(null, msg.toString(), e);
208         }
209         ClassUtil.throwIfIOE(e);
210         ClassUtil.throwIfRTE(e);
211         // let's wrap the innermost problem
212         Throwable t = ClassUtil.getRootCause(e);
213         throw new JsonMappingException(null, ClassUtil.exceptionMessage(t), t);
214     }
215 
getClassName()216     private String getClassName() { return _setter.getDeclaringClass().getName(); }
217 
toString()218     @Override public String toString() { return "[any property on class "+getClassName()+"]"; }
219 
220     private static class AnySetterReferring extends Referring {
221         private final SettableAnyProperty _parent;
222         private final Object _pojo;
223         private final String _propName;
224 
AnySetterReferring(SettableAnyProperty parent, UnresolvedForwardReference reference, Class<?> type, Object instance, String propName)225         public AnySetterReferring(SettableAnyProperty parent,
226                 UnresolvedForwardReference reference, Class<?> type, Object instance, String propName)
227         {
228             super(reference, type);
229             _parent = parent;
230             _pojo = instance;
231             _propName = propName;
232         }
233 
234         @Override
handleResolvedForwardReference(Object id, Object value)235         public void handleResolvedForwardReference(Object id, Object value)
236             throws IOException
237         {
238             if (!hasId(id)) {
239                 throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id.toString()
240                         + "] that wasn't previously registered.");
241             }
242             _parent.set(_pojo, _propName, value);
243         }
244     }
245 }
246