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