org.codehaus.jackson.map.ObjectReader Maven / Gradle / Ivy
package org.codehaus.jackson.map;
import java.io.*;
import java.net.URL;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.SerializedString;
import org.codehaus.jackson.map.deser.StdDeserializationContext;
import org.codehaus.jackson.map.type.SimpleType;
import org.codehaus.jackson.node.JsonNodeFactory;
import org.codehaus.jackson.node.NullNode;
import org.codehaus.jackson.node.TreeTraversingParser;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
import org.codehaus.jackson.util.VersionUtil;
/**
* Builder object that can be used for per-serialization configuration of
* deserialization parameters, such as root type to use or object
* to update (instead of constructing new instance).
* Uses "fluid" (aka builder) pattern so that instances are immutable
* (and thus fully thread-safe with no external synchronization);
* new instances are constructed for different configurations.
* Instances are initially constructed by {@link ObjectMapper} and can be
* reused.
*
* @author tatu
* @since 1.6
*/
public class ObjectReader
extends ObjectCodec
implements Versioned
{
private final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class);
/*
/**********************************************************
/* Immutable configuration from ObjectMapper
/**********************************************************
*/
/**
* General serialization configuration settings; while immutable,
* can use copy-constructor to create modified instances as necessary.
*/
protected final DeserializationConfig _config;
/**
* Flag that indicates whether root values are expected to be unwrapped
* or not
*
* @since 1.9
*/
protected final boolean _unwrapRoot;
/**
* Root-level cached deserializers
*/
final protected ConcurrentHashMap> _rootDeserializers;
protected final DeserializerProvider _provider;
/**
* Factory used for constructing {@link JsonGenerator}s
*/
protected final JsonFactory _jsonFactory;
/*
/**********************************************************
/* Configuration that can be changed during building
/**********************************************************
*/
/**
* Declared type of value to instantiate during deserialization.
* Defines which deserializer to use; as well as base type of instance
* to construct if an updatable value is not configured to be used
* (subject to changes by embedded type information, for polymorphic
* types). If {@link #_valueToUpdate} is non-null, only used for
* locating deserializer.
*/
protected final JavaType _valueType;
/**
* Instance to update with data binding; if any. If null,
* a new instance is created, if non-null, properties of
* this value object will be updated instead.
* Note that value can be of almost any type, except not
* {@link org.codehaus.jackson.map.type.ArrayType}; array
* types can not be modified because array size is immutable.
*/
protected final Object _valueToUpdate;
/**
* When using data format that uses a schema, schema is passed
* to parser.
*
* @since 1.8
*/
protected final FormatSchema _schema;
/**
* Values that can be injected during deserialization, if any.
*
* @since 1.9
*/
protected final InjectableValues _injectableValues;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* Constructor used by {@link ObjectMapper} for initial instantiation
*
* @since 1.8
*/
protected ObjectReader(ObjectMapper mapper, DeserializationConfig config)
{
this(mapper, config, null, null, null, null);
}
protected ObjectReader(ObjectMapper mapper, DeserializationConfig config,
JavaType valueType, Object valueToUpdate, FormatSchema schema,
InjectableValues injectableValues)
{
_config = config;
_rootDeserializers = mapper._rootDeserializers;
_provider = mapper._deserializerProvider;
_jsonFactory = mapper._jsonFactory;
_valueType = valueType;
_valueToUpdate = valueToUpdate;
if (valueToUpdate != null && valueType.isArrayType()) {
throw new IllegalArgumentException("Can not update an array value");
}
_schema = schema;
_injectableValues = injectableValues;
_unwrapRoot = config.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE);
}
/**
* Copy constructor used for building variations.
*/
protected ObjectReader(ObjectReader base, DeserializationConfig config,
JavaType valueType, Object valueToUpdate, FormatSchema schema,
InjectableValues injectableValues)
{
_config = config;
_rootDeserializers = base._rootDeserializers;
_provider = base._provider;
_jsonFactory = base._jsonFactory;
_valueType = valueType;
_valueToUpdate = valueToUpdate;
if (valueToUpdate != null && valueType.isArrayType()) {
throw new IllegalArgumentException("Can not update an array value");
}
_schema = schema;
_injectableValues = injectableValues;
_unwrapRoot = config.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE);
}
/**
* Method that will return version information stored in and read from jar
* that contains this class.
*
* @since 1.6
*/
@Override
public Version version() {
return VersionUtil.versionFor(getClass());
}
/**
* Method for constructing a new reader instance that is configured
* to data bind into specified type.
*
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*/
public ObjectReader withType(JavaType valueType)
{
if (valueType == _valueType) return this;
// type is stored here, no need to make a copy of config
return new ObjectReader(this, _config, valueType, _valueToUpdate,
_schema, _injectableValues);
}
/**
* Method for constructing a new reader instance that is configured
* to data bind into specified type.
*
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*/
public ObjectReader withType(Class> valueType)
{
return withType(_config.constructType(valueType));
}
/**
* Method for constructing a new reader instance that is configured
* to data bind into specified type.
*
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*/
public ObjectReader withType(java.lang.reflect.Type valueType)
{
return withType(_config.getTypeFactory().constructType(valueType));
}
/**
* Method for constructing a new reader instance that is configured
* to data bind into specified type.
*
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*
* @since 1.8
*/
public ObjectReader withType(TypeReference> valueTypeRef)
{
return withType(_config.getTypeFactory().constructType(valueTypeRef.getType()));
}
/**
* Method for constructing a new reader instance with configuration that uses
* passed {@link JsonNodeFactory} for constructing {@link JsonNode}
* instances.
*
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*/
public ObjectReader withNodeFactory(JsonNodeFactory f)
{
// node factory is stored within config, so need to copy that first
if (f == _config.getNodeFactory()) return this;
return new ObjectReader(this, _config.withNodeFactory(f), _valueType, _valueToUpdate,
_schema, _injectableValues);
}
/**
* Method for constructing a new instance with configuration that
* updates passed Object (as root value), instead of constructing
* a new value.
*
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*/
public ObjectReader withValueToUpdate(Object value)
{
if (value == _valueToUpdate) return this;
if (value == null) {
throw new IllegalArgumentException("cat not update null value");
}
JavaType t = (_valueType == null) ? _config.constructType(value.getClass()) : _valueType;
return new ObjectReader(this, _config, t, value,
_schema, _injectableValues);
}
/**
* Method for constructing a new instance with configuration that
* passes specified {@link FormatSchema} to {@link JsonParser} that
* is constructed for parsing content.
*
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*
* @since 1.8
*/
public ObjectReader withSchema(FormatSchema schema)
{
if (_schema == schema) {
return this;
}
return new ObjectReader(this, _config, _valueType, _valueToUpdate,
schema, _injectableValues);
}
/**
* Method for constructing a new instance with configuration that uses
* passed {@link InjectableValues} to provide injectable values.
*
* Note that the method does NOT change state of this reader, but
* rather construct and returns a newly configured instance.
*
* @since 1.9
*/
public ObjectReader withInjectableValues(InjectableValues injectableValues)
{
if (_injectableValues == injectableValues) {
return this;
}
return new ObjectReader(this, _config, _valueType, _valueToUpdate,
_schema, injectableValues);
}
/*
/**********************************************************
/* Deserialization methods; basic ones to support ObjectCodec first
/* (ones that take JsonParser)
/**********************************************************
*/
/**
* Method that binds content read using given parser, using
* configuration of this reader, including expected result type.
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
public T readValue(JsonParser jp)
throws IOException, JsonProcessingException
{
return (T) _bind(jp);
}
/**
* Convenience method that binds content read using given parser, using
* configuration of this reader, except that expected value type
* is specified with the call (instead of currently configured root type).
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
@Override
public T readValue(JsonParser jp, Class valueType)
throws IOException, JsonProcessingException
{
return (T) withType(valueType).readValue(jp);
}
/**
* Convenience method that binds content read using given parser, using
* configuration of this reader, except that expected value type
* is specified with the call (instead of currently configured root type).
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
@Override
public T readValue(JsonParser jp, TypeReference> valueTypeRef)
throws IOException, JsonProcessingException
{
return (T) withType(valueTypeRef).readValue(jp);
}
/**
* Convenience method that binds content read using given parser, using
* configuration of this reader, except that expected value type
* is specified with the call (instead of currently configured root type).
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
@Override
public T readValue(JsonParser jp, JavaType valueType) throws IOException, JsonProcessingException {
return (T) withType(valueType).readValue(jp);
}
/**
* Convenience method that binds content read using given parser, using
* configuration of this reader, except that content is bound as
* JSON tree instead of configured root value type.
*
* Note: if an object was specified with {@link #withValueToUpdate}, it
* will be ignored.
*/
@Override
public JsonNode readTree(JsonParser jp)
throws IOException, JsonProcessingException
{
return _bindAsTree(jp);
}
/**
* Convenience method that is equivalent to:
*
* withType(valueType).readValues(jp);
*
*/
@Override
public Iterator readValues(JsonParser jp, Class valueType)
throws IOException, JsonProcessingException {
return withType(valueType).readValues(jp);
}
/**
* Convenience method that is equivalent to:
*
* withType(valueTypeRef).readValues(jp);
*
*/
@Override
public Iterator readValues(JsonParser jp, TypeReference> valueTypeRef)
throws IOException, JsonProcessingException {
return withType(valueTypeRef).readValues(jp);
}
/**
* Convenience method that is equivalent to:
*
* withType(valueType).readValues(jp);
*
*/
@Override
public Iterator readValues(JsonParser jp, JavaType valueType)
throws IOException, JsonProcessingException {
return withType(valueType).readValues(jp);
}
/*
/**********************************************************
/* Deserialization methods; others similar to what ObjectMapper has
/**********************************************************
*/
/**
* Method that binds content read from given input source,
* using configuration of this reader.
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
public T readValue(InputStream src)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(_jsonFactory.createJsonParser(src));
}
/**
* Method that binds content read from given input source,
* using configuration of this reader.
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
public T readValue(Reader src)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(_jsonFactory.createJsonParser(src));
}
/**
* Method that binds content read from given JSON string,
* using configuration of this reader.
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
public T readValue(String src)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(_jsonFactory.createJsonParser(src));
}
/**
* Method that binds content read from given byte array,
* using configuration of this reader.
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
public T readValue(byte[] src)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(_jsonFactory.createJsonParser(src));
}
/**
* Method that binds content read from given byte array,
* using configuration of this reader.
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
public T readValue(byte[] src, int offset, int length)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(_jsonFactory.createJsonParser(src, offset, length));
}
@SuppressWarnings("unchecked")
public T readValue(File src)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(_jsonFactory.createJsonParser(src));
}
/**
* Method that binds content read from given input source,
* using configuration of this reader.
* Value return is either newly constructed, or root value that
* was specified with {@link #withValueToUpdate(Object)}.
*/
@SuppressWarnings("unchecked")
public T readValue(URL src)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(_jsonFactory.createJsonParser(src));
}
/**
* Convenience method for converting results from given JSON tree into given
* value type. Basically short-cut for:
*
* objectReader.readValue(src.traverse())
*
*/
@SuppressWarnings("unchecked")
public T readValue(JsonNode src)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(treeAsTokens(src));
}
/**
* Method that reads content from given input source,
* using configuration of this reader, and binds it as JSON Tree.
*
* Note that if an object was specified with a call to
* {@link #withValueToUpdate(Object)}
* it will just be ignored; result is always a newly constructed
* {@link JsonNode} instance.
*/
public JsonNode readTree(InputStream in)
throws IOException, JsonProcessingException
{
return _bindAndCloseAsTree(_jsonFactory.createJsonParser(in));
}
/**
* Method that reads content from given input source,
* using configuration of this reader, and binds it as JSON Tree.
*
* Note that if an object was specified with a call to
* {@link #withValueToUpdate(Object)}
* it will just be ignored; result is always a newly constructed
* {@link JsonNode} instance.
*/
public JsonNode readTree(Reader r)
throws IOException, JsonProcessingException
{
return _bindAndCloseAsTree(_jsonFactory.createJsonParser(r));
}
/**
* Method that reads content from given JSON input String,
* using configuration of this reader, and binds it as JSON Tree.
*
* Note that if an object was specified with a call to
* {@link #withValueToUpdate(Object)}
* it will just be ignored; result is always a newly constructed
* {@link JsonNode} instance.
*/
public JsonNode readTree(String content)
throws IOException, JsonProcessingException
{
return _bindAndCloseAsTree(_jsonFactory.createJsonParser(content));
}
/*
/**********************************************************
/* Deserialization methods; reading sequence of values
/**********************************************************
*/
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public MappingIterator readValues(JsonParser jp)
throws IOException, JsonProcessingException
{
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
// false -> do not close as caller gave parser instance
return new MappingIterator(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType),
false, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public MappingIterator readValues(InputStream src)
throws IOException, JsonProcessingException
{
JsonParser jp = _jsonFactory.createJsonParser(src);
if (_schema != null) {
jp.setSchema(_schema);
}
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
return new MappingIterator(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType),
true, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public MappingIterator readValues(Reader src)
throws IOException, JsonProcessingException
{
JsonParser jp = _jsonFactory.createJsonParser(src);
if (_schema != null) {
jp.setSchema(_schema);
}
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
return new MappingIterator(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public MappingIterator readValues(String json)
throws IOException, JsonProcessingException
{
JsonParser jp = _jsonFactory.createJsonParser(json);
if (_schema != null) {
jp.setSchema(_schema);
}
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
return new MappingIterator(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public MappingIterator readValues(byte[] src, int offset, int length)
throws IOException, JsonProcessingException
{
JsonParser jp = _jsonFactory.createJsonParser(src, offset, length);
if (_schema != null) {
jp.setSchema(_schema);
}
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
return new MappingIterator(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/**
* Since 1.9.3
*/
public final MappingIterator readValues(byte[] src)
throws IOException, JsonProcessingException {
return readValues(src, 0, src.length);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public MappingIterator readValues(File src)
throws IOException, JsonProcessingException
{
JsonParser jp = _jsonFactory.createJsonParser(src);
if (_schema != null) {
jp.setSchema(_schema);
}
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
return new MappingIterator(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public MappingIterator readValues(URL src)
throws IOException, JsonProcessingException
{
JsonParser jp = _jsonFactory.createJsonParser(src);
if (_schema != null) {
jp.setSchema(_schema);
}
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
return new MappingIterator(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
/**
* Actual implementation of value reading+binding operation.
*/
protected Object _bind(JsonParser jp)
throws IOException, JsonParseException, JsonMappingException
{
/* First: may need to read the next token, to initialize state (either
* before first read from parser, or after previous token has been cleared)
*/
Object result;
JsonToken t = _initForReading(jp);
if (t == JsonToken.VALUE_NULL) {
if (_valueToUpdate == null) {
result = _findRootDeserializer(_config, _valueType).getNullValue();
} else {
result = _valueToUpdate;
}
} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = _valueToUpdate;
} else { // pointing to event other than null
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
JsonDeserializer