All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.jackson.map.deser.std.MapDeserializer Maven / Gradle / Ivy

Go to download

Data Mapper package is a high-performance data binding package built on Jackson JSON processor

The newest version!
package org.codehaus.jackson.map.deser.std;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.annotate.JacksonStdImpl;
import org.codehaus.jackson.map.deser.SettableBeanProperty;
import org.codehaus.jackson.map.deser.ValueInstantiator;
import org.codehaus.jackson.map.deser.impl.PropertyBasedCreator;
import org.codehaus.jackson.map.deser.impl.PropertyValueBuffer;
import org.codehaus.jackson.map.deser.std.ContainerDeserializerBase;
import org.codehaus.jackson.map.deser.std.StdValueInstantiator;
import org.codehaus.jackson.map.introspect.AnnotatedConstructor;
import org.codehaus.jackson.map.introspect.AnnotatedWithParams;
import org.codehaus.jackson.map.util.ArrayBuilders;
import org.codehaus.jackson.type.JavaType;

/**
 * Basic serializer that can take Json "Object" structure and
 * construct a {@link java.util.Map} instance, with typed contents.
 *

* Note: for untyped content (one indicated by passing Object.class * as the type), {@link UntypedObjectDeserializer} is used instead. * It can also construct {@link java.util.Map}s, but not with specific * POJO types, only other containers and primitives/wrappers. */ @JacksonStdImpl public class MapDeserializer extends ContainerDeserializerBase> implements ResolvableDeserializer { // // Configuration: typing, deserializers protected final JavaType _mapType; /** * Key deserializer used, if not null. If null, String from JSON * content is used as is. */ protected final KeyDeserializer _keyDeserializer; /** * Value deserializer. */ protected final JsonDeserializer _valueDeserializer; /** * If value instances have polymorphic type information, this * is the type deserializer that can handle it */ protected final TypeDeserializer _valueTypeDeserializer; // // Instance construction settings: /** * @since 1.9 */ protected final ValueInstantiator _valueInstantiator; /** * @since 1.9 */ protected final boolean _hasDefaultCreator; /** * If the Map is to be instantiated using non-default constructor * or factory method * that takes one or more named properties as argument(s), * this creator is used for instantiation. */ protected PropertyBasedCreator _propertyBasedCreator; /** * Deserializer that is used iff delegate-based creator is * to be used for deserializing from JSON Object. */ protected JsonDeserializer _delegateDeserializer; // // Any properties to ignore if seen? protected HashSet _ignorableProperties; /* /********************************************************** /* Life-cycle /********************************************************** */ /** * @deprecated Since 1.9, use variant that takes ValueInstantiator */ @Deprecated protected MapDeserializer(JavaType mapType, Constructor> defCtor, KeyDeserializer keyDeser, JsonDeserializer valueDeser, TypeDeserializer valueTypeDeser) { super(Map.class); _mapType = mapType; _keyDeserializer = keyDeser; _valueDeserializer = valueDeser; _valueTypeDeserializer = valueTypeDeser; // not super-clean, but has to do... StdValueInstantiator inst = new StdValueInstantiator(null, mapType); if (defCtor != null) { AnnotatedConstructor aCtor = new AnnotatedConstructor(defCtor, null, null); inst.configureFromObjectSettings(aCtor, null, null, null, null); } _hasDefaultCreator = (defCtor != null); _valueInstantiator = inst; } public MapDeserializer(JavaType mapType, ValueInstantiator valueInstantiator, KeyDeserializer keyDeser, JsonDeserializer valueDeser, TypeDeserializer valueTypeDeser) { super(Map.class); _mapType = mapType; _keyDeserializer = keyDeser; _valueDeserializer = valueDeser; _valueTypeDeserializer = valueTypeDeser; _valueInstantiator = valueInstantiator; if (valueInstantiator.canCreateFromObjectWith()) { _propertyBasedCreator = new PropertyBasedCreator(valueInstantiator); } else { _propertyBasedCreator = null; } _hasDefaultCreator = valueInstantiator.canCreateUsingDefault(); } /** * Copy-constructor that can be used by sub-classes to allow * copy-on-write styling copying of settings of an existing instance. * * @since 1.9 */ protected MapDeserializer(MapDeserializer src) { super(src._valueClass); _mapType = src._mapType; _keyDeserializer = src._keyDeserializer; _valueDeserializer = src._valueDeserializer; _valueTypeDeserializer = src._valueTypeDeserializer; _valueInstantiator = src._valueInstantiator; _propertyBasedCreator = src._propertyBasedCreator; _delegateDeserializer = src._delegateDeserializer; _hasDefaultCreator = src._hasDefaultCreator; // should we make a copy here? _ignorableProperties = src._ignorableProperties; } public void setIgnorableProperties(String[] ignorable) { _ignorableProperties = (ignorable == null || ignorable.length == 0) ? null : ArrayBuilders.arrayToSet(ignorable); } /* /********************************************************** /* Validation, post-processing (ResolvableDeserializer) /********************************************************** */ /** * Method called to finalize setup of this deserializer, * after deserializer itself has been registered. This * is needed to handle recursive and transitive dependencies. */ @Override public void resolve(DeserializationConfig config, DeserializerProvider provider) throws JsonMappingException { // May need to resolve types for delegate- and/or property-based creators: if (_valueInstantiator.canCreateUsingDelegate()) { JavaType delegateType = _valueInstantiator.getDelegateType(); if (delegateType == null) { throw new IllegalArgumentException("Invalid delegate-creator definition for "+_mapType +": value instantiator ("+_valueInstantiator.getClass().getName() +") returned true for 'canCreateUsingDelegate()', but null for 'getDelegateType()'"); } AnnotatedWithParams delegateCreator = _valueInstantiator.getDelegateCreator(); // Need to create a temporary property to allow contextual deserializers: // Note: unlike BeanDeserializer, we don't have an AnnotatedClass around; hence no annotations passed BeanProperty.Std property = new BeanProperty.Std(null, delegateType, null, delegateCreator); _delegateDeserializer = findDeserializer(config, provider, delegateType, property); } if (_propertyBasedCreator != null) { for (SettableBeanProperty prop : _propertyBasedCreator.getCreatorProperties()) { if (!prop.hasValueDeserializer()) { _propertyBasedCreator.assignDeserializer(prop, findDeserializer(config, provider, prop.getType(), prop)); } } } } /* /********************************************************** /* ContainerDeserializerBase API /********************************************************** */ @Override public JavaType getContentType() { return _mapType.getContentType(); } @Override public JsonDeserializer getContentDeserializer() { return _valueDeserializer; } /* /********************************************************** /* JsonDeserializer API /********************************************************** */ @Override @SuppressWarnings("unchecked") public Map deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { if (_propertyBasedCreator != null) { return _deserializeUsingCreator(jp, ctxt); } if (_delegateDeserializer != null) { return (Map) _valueInstantiator.createUsingDelegate(_delegateDeserializer.deserialize(jp, ctxt)); } if (!_hasDefaultCreator) { throw ctxt.instantiationException(getMapClass(), "No default constructor found"); } // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT JsonToken t = jp.getCurrentToken(); if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) { // [JACKSON-620] (empty) String may be ok however: if (t == JsonToken.VALUE_STRING) { return (Map) _valueInstantiator.createFromString(jp.getText()); } throw ctxt.mappingException(getMapClass()); } final Map result = (Map) _valueInstantiator.createUsingDefault(); _readAndBind(jp, ctxt, result); return result; } @Override public Map deserialize(JsonParser jp, DeserializationContext ctxt, Map result) throws IOException, JsonProcessingException { // Ok: must point to START_OBJECT or FIELD_NAME JsonToken t = jp.getCurrentToken(); if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) { throw ctxt.mappingException(getMapClass()); } _readAndBind(jp, ctxt, result); return result; } @Override public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException, JsonProcessingException { // In future could check current token... for now this should be enough: return typeDeserializer.deserializeTypedFromObject(jp, ctxt); } /* /********************************************************** /* Other public accessors /********************************************************** */ @SuppressWarnings("unchecked") public final Class getMapClass() { return (Class>) _mapType.getRawClass(); } @Override public JavaType getValueType() { return _mapType; } /* /********************************************************** /* Internal methods /********************************************************** */ protected final void _readAndBind(JsonParser jp, DeserializationContext ctxt, Map result) throws IOException, JsonProcessingException { JsonToken t = jp.getCurrentToken(); if (t == JsonToken.START_OBJECT) { t = jp.nextToken(); } final KeyDeserializer keyDes = _keyDeserializer; final JsonDeserializer valueDes = _valueDeserializer; final TypeDeserializer typeDeser = _valueTypeDeserializer; for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { // Must point to field name String fieldName = jp.getCurrentName(); Object key = keyDes.deserializeKey(fieldName, ctxt); // And then the value... t = jp.nextToken(); if (_ignorableProperties != null && _ignorableProperties.contains(fieldName)) { jp.skipChildren(); continue; } // Note: must handle null explicitly here; value deserializers won't Object value; if (t == JsonToken.VALUE_NULL) { value = null; } else if (typeDeser == null) { value = valueDes.deserialize(jp, ctxt); } else { value = valueDes.deserializeWithType(jp, ctxt, typeDeser); } /* !!! 23-Dec-2008, tatu: should there be an option to verify * that there are no duplicate field names? (and/or what * to do, keep-first or keep-last) */ result.put(key, value); } } @SuppressWarnings("unchecked") public Map _deserializeUsingCreator(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { final PropertyBasedCreator creator = _propertyBasedCreator; PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt); JsonToken t = jp.getCurrentToken(); if (t == JsonToken.START_OBJECT) { t = jp.nextToken(); } final JsonDeserializer valueDes = _valueDeserializer; final TypeDeserializer typeDeser = _valueTypeDeserializer; for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { String propName = jp.getCurrentName(); t = jp.nextToken(); // to get to value if (_ignorableProperties != null && _ignorableProperties.contains(propName)) { jp.skipChildren(); // and skip it (in case of array/object) continue; } // creator property? SettableBeanProperty prop = creator.findCreatorProperty(propName); if (prop != null) { // Last property to set? Object value = prop.deserialize(jp, ctxt); if (buffer.assignParameter(prop.getPropertyIndex(), value)) { jp.nextToken(); Map result; try { result = (Map)creator.build(buffer); } catch (Exception e) { wrapAndThrow(e, _mapType.getRawClass()); return null; } _readAndBind(jp, ctxt, result); return result; } continue; } // other property? needs buffering String fieldName = jp.getCurrentName(); Object key = _keyDeserializer.deserializeKey(fieldName, ctxt); Object value; if (t == JsonToken.VALUE_NULL) { value = null; } else if (typeDeser == null) { value = valueDes.deserialize(jp, ctxt); } else { value = valueDes.deserializeWithType(jp, ctxt, typeDeser); } buffer.bufferMapProperty(key, value); } // end of JSON object? // if so, can just construct and leave... try { return (Map)creator.build(buffer); } catch (Exception e) { wrapAndThrow(e, _mapType.getRawClass()); return null; } } // note: copied form BeanDeserializer; should try to share somehow... protected void wrapAndThrow(Throwable t, Object ref) throws IOException { // to handle StackOverflow: while (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } // Errors and "plain" IOExceptions to be passed as is if (t instanceof Error) { throw (Error) t; } // ... except for mapping exceptions if (t instanceof IOException && !(t instanceof JsonMappingException)) { throw (IOException) t; } throw JsonMappingException.wrapWithPath(t, ref, null); } }