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

com.fasterxml.jackson.databind.ObjectReader Maven / Gradle / Ivy

There is a newer version: 1.5.7
Show newest version
package com.fasterxml.jackson.databind;

import java.io.*;
import java.net.URL;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.ResolvedType;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
import com.fasterxml.jackson.databind.deser.DataFormatReaders;
import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.TreeTraversingParser;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.RootNameLookup;

/**
 * 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 "fluent" (or, kind of, 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, shared, cached; both because of thread-safety and because * instances are relatively light-weight. */ public class ObjectReader extends ObjectCodec implements Versioned, java.io.Serializable // since 2.1 { private static final long serialVersionUID = -4251443320039569153L; 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; /** * Blueprint instance of deserialization context; used for creating * actual instance when needed. */ protected final DefaultDeserializationContext _context; /** * Factory used for constructing {@link JsonGenerator}s */ protected final JsonFactory _parserFactory; /** * Flag that indicates whether root values are expected to be unwrapped or not */ protected final boolean _unwrapRoot; /* /********************************************************** /* 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; /** * We may pre-fetch deserializer as soon as {@link #_valueType} * is known, and if so, reuse it afterwards. * This allows avoiding further deserializer lookups and increases * performance a bit on cases where readers are reused. * * @since 2.1 */ protected final JsonDeserializer _rootDeserializer; /** * 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 com.fasterxml.jackson.databind.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. */ protected final FormatSchema _schema; /** * Values that can be injected during deserialization, if any. */ protected final InjectableValues _injectableValues; /** * Optional detector used for auto-detecting data format that byte-based * input uses. *

* NOTE: If defined non-null, readValue() methods that take * {@link Reader} or {@link String} input will fail with exception, * because format-detection only works on byte-sources. Also, if format * can not be detect reliably (as per detector settings), * a {@link JsonParseException} will be thrown). * * @since 2.1 */ protected final DataFormatReaders _dataFormatReaders; /* /********************************************************** /* Caching /********************************************************** */ /** * Root-level cached deserializers */ final protected ConcurrentHashMap> _rootDeserializers; /** * Cache for root names used when root-wrapping is enabled. */ protected final RootNameLookup _rootNames; /* /********************************************************** /* Life-cycle, construction /********************************************************** */ /** * Constructor used by {@link ObjectMapper} for initial instantiation */ protected ObjectReader(ObjectMapper mapper, DeserializationConfig config) { this(mapper, config, null, null, null, null); } /** * Constructor called when a root deserializer should be fetched based * on other configuration. */ protected ObjectReader(ObjectMapper mapper, DeserializationConfig config, JavaType valueType, Object valueToUpdate, FormatSchema schema, InjectableValues injectableValues) { _config = config; _context = mapper._deserializationContext; _rootDeserializers = mapper._rootDeserializers; _parserFactory = mapper._jsonFactory; _rootNames = mapper._rootNames; _valueType = valueType; _valueToUpdate = valueToUpdate; if (valueToUpdate != null && valueType.isArrayType()) { throw new IllegalArgumentException("Can not update an array value"); } _schema = schema; _injectableValues = injectableValues; _unwrapRoot = config.useRootWrapping(); _rootDeserializer = _prefetchRootDeserializer(config, valueType); _dataFormatReaders = null; } /** * Copy constructor used for building variations. */ protected ObjectReader(ObjectReader base, DeserializationConfig config, JavaType valueType, JsonDeserializer rootDeser, Object valueToUpdate, FormatSchema schema, InjectableValues injectableValues, DataFormatReaders dataFormatReaders) { _config = config; _context = base._context; _rootDeserializers = base._rootDeserializers; _parserFactory = base._parserFactory; _rootNames = base._rootNames; _valueType = valueType; _rootDeserializer = rootDeser; _valueToUpdate = valueToUpdate; if (valueToUpdate != null && valueType.isArrayType()) { throw new IllegalArgumentException("Can not update an array value"); } _schema = schema; _injectableValues = injectableValues; _unwrapRoot = config.useRootWrapping(); _dataFormatReaders = dataFormatReaders; } /** * Copy constructor used when modifying simple feature flags */ protected ObjectReader(ObjectReader base, DeserializationConfig config) { _config = config; _context = base._context; _rootDeserializers = base._rootDeserializers; _parserFactory = base._parserFactory; _rootNames = base._rootNames; _valueType = base._valueType; _rootDeserializer = base._rootDeserializer; _valueToUpdate = base._valueToUpdate; _schema = base._schema; _injectableValues = base._injectableValues; _unwrapRoot = config.useRootWrapping(); _dataFormatReaders = base._dataFormatReaders; } protected ObjectReader(ObjectReader base, JsonFactory f) { // may need to override ordering, based on data format capabilities _config = base._config .with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, f.requiresPropertyOrdering()); _context = base._context; _rootDeserializers = base._rootDeserializers; _parserFactory = f; _rootNames = base._rootNames; _valueType = base._valueType; _rootDeserializer = base._rootDeserializer; _valueToUpdate = base._valueToUpdate; _schema = base._schema; _injectableValues = base._injectableValues; _unwrapRoot = base._unwrapRoot; _dataFormatReaders = base._dataFormatReaders; } /** * Method that will return version information stored in and read from jar * that contains this class. */ @Override public Version version() { return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; } /* /********************************************************** /* Life-cycle, fluent factory methods /********************************************************** */ public ObjectReader with(DeserializationConfig config) { return _with(config); } /** * Method for constructing a new reader instance that is configured * with specified feature enabled. */ public ObjectReader with(DeserializationFeature feature) { return _with(_config.with(feature)); } /** * Method for constructing a new reader instance that is configured * with specified features enabled. */ public ObjectReader with(DeserializationFeature first, DeserializationFeature... other) { return _with(_config.with(first, other)); } /** * Method for constructing a new reader instance that is configured * with specified features enabled. */ public ObjectReader withFeatures(DeserializationFeature... features) { return _with(_config.withFeatures(features)); } /** * Method for constructing a new reader instance that is configured * with specified feature disabled. */ public ObjectReader without(DeserializationFeature feature) { return _with(_config.without(feature)); } /** * Method for constructing a new reader instance that is configured * with specified features disabled. */ public ObjectReader without(DeserializationFeature first, DeserializationFeature... other) { return _with(_config.without(first, other)); } /** * Method for constructing a new reader instance that is configured * with specified features disabled. */ public ObjectReader withoutFeatures(DeserializationFeature... features) { return _with(_config.withoutFeatures(features)); } /** * 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. */ public ObjectReader with(InjectableValues injectableValues) { if (_injectableValues == injectableValues) { return this; } return new ObjectReader(this, _config, _valueType, _rootDeserializer, _valueToUpdate, _schema, injectableValues, _dataFormatReaders); } /** * 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 with(JsonNodeFactory f) { return _with(_config.with(f)); } /** * Method for constructing a new reader instance with configuration that uses * passed {@link JsonFactory} for constructing underlying Readers. *

* NOTE: only factories that DO NOT REQUIRE SPECIAL MAPPERS * (that is, ones that return false for * {@link JsonFactory#requiresCustomCodec()}) can be used: trying * to use one that requires custom codec will throw exception * * @since 2.1 */ public ObjectReader with(JsonFactory f) { if (f == _parserFactory) { return this; } ObjectReader r = new ObjectReader(this, f); // Also, try re-linking, if possible... if (f.getCodec() == null) { f.setCodec(r); } return r; } /** * Method for constructing a new instance with configuration that * specifies what root name to expect for "root name unwrapping". * See {@link DeserializationConfig#withRootName(String)} for * details. *

* Note that the method does NOT change state of this reader, but * rather construct and returns a newly configured instance. */ public ObjectReader withRootName(String rootName) { return _with(_config.withRootName(rootName)); } /** * 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. */ public ObjectReader with(FormatSchema schema) { if (_schema == schema) { return this; } _verifySchemaType(schema); return new ObjectReader(this, _config, _valueType, _rootDeserializer, _valueToUpdate, schema, _injectableValues, _dataFormatReaders); } /** * 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 != null && valueType.equals(_valueType)) { return this; } JsonDeserializer rootDeser = _prefetchRootDeserializer(_config, valueType); // type is stored here, no need to make a copy of config DataFormatReaders det = _dataFormatReaders; if (det != null) { det = det.withType(valueType); } return new ObjectReader(this, _config, valueType, rootDeser, _valueToUpdate, _schema, _injectableValues, det); } /** * 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. */ public ObjectReader withType(TypeReference valueTypeRef) { return withType(_config.getTypeFactory().constructType(valueTypeRef.getType())); } /** * 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; /* no real benefit from pre-fetching, as updating readers are much * less likely to be reused, and value type may also be forced * with a later chained call... */ if (_valueType == null) { t = _config.constructType(value.getClass()); } else { t = _valueType; } return new ObjectReader(this, _config, t, _rootDeserializer, value, _schema, _injectableValues, _dataFormatReaders); } /** * Method for constructing a new instance with configuration that * uses specified View for filtering. *

* Note that the method does NOT change state of this reader, but * rather construct and returns a newly configured instance. */ public ObjectReader withView(Class activeView) { return _with(_config.withView(activeView)); } public ObjectReader with(Locale l) { return _with(_config.with(l)); } public ObjectReader with(TimeZone tz) { return _with(_config.with(tz)); } public ObjectReader withHandler(DeserializationProblemHandler h) { return _with(_config.withHandler(h)); } public ObjectReader with(Base64Variant defaultBase64) { return _with(_config.with(defaultBase64)); } /** * Fluent factory method for constructing a reader that will try to * auto-detect underlying data format, using specified list of * {@link JsonFactory} instances, and default {@link DataFormatReaders} settings * (for customized {@link DataFormatReaders}, you can construct instance yourself). * to construct appropriate {@link JsonParser} for actual parsing. *

* Note: since format detection only works with byte sources, it is possible to * get a failure from some 'readValue()' methods. Also, if input can not be reliably * (enough) detected as one of specified types, an exception will be thrown. *

* Note: not all {@link JsonFactory} types can be passed: specifically, ones that * require "custom codec" (like XML factory) will not work. Instead, use * method that takes {@link ObjectReader} instances instead of factories. * * @param readers Data formats accepted, in decreasing order of priority (that is, * matches checked in listed order, first match wins) * * @return Newly configured writer instance * * @since 2.1 */ public ObjectReader withFormatDetection(ObjectReader... readers) { return withFormatDetection(new DataFormatReaders(readers)); } /** * Fluent factory method for constructing a reader that will try to * auto-detect underlying data format, using specified * {@link DataFormatReaders}. *

* NOTE: since format detection only works with byte sources, it is possible to * get a failure from some 'readValue()' methods. Also, if input can not be reliably * (enough) detected as one of specified types, an exception will be thrown. * * @param readers DataFormatReaders to use for detecting underlying format. * * @return Newly configured writer instance * * @since 2.1 */ public ObjectReader withFormatDetection(DataFormatReaders readers) { return new ObjectReader(this, _config, _valueType, _rootDeserializer, _valueToUpdate, _schema, _injectableValues, readers); } /** * @since 2.3 */ public ObjectReader with(ContextAttributes attrs) { DeserializationConfig newConfig = _config.with(attrs); return (newConfig == _config) ? this : new ObjectReader(this, newConfig); } /** * @since 2.3 */ public ObjectReader withAttributes(Map attrs) { DeserializationConfig newConfig = _config.withAttributes(attrs); return (newConfig == _config) ? this : new ObjectReader(this, newConfig); } /** * @since 2.3 */ public ObjectReader withAttribute(Object key, Object value) { DeserializationConfig newConfig = _config.withAttribute(key, value); return (newConfig == _config) ? this : new ObjectReader(this, newConfig); } /** * @since 2.3 */ public ObjectReader withoutAttribute(Object key) { DeserializationConfig newConfig = _config.withoutAttribute(key); return (newConfig == _config) ? this : new ObjectReader(this, newConfig); } /* /********************************************************** /* Simple accessors /********************************************************** */ public boolean isEnabled(DeserializationFeature f) { return _config.isEnabled(f); } public boolean isEnabled(MapperFeature f) { return _config.isEnabled(f); } public boolean isEnabled(JsonParser.Feature f) { return _parserFactory.isEnabled(f); } /** * @since 2.2 */ public DeserializationConfig getConfig() { return _config; } /** * @since 2.1 */ @Override public JsonFactory getFactory() { return _parserFactory; } /** * @deprecated Since 2.1: Use {@link #getFactory} instead */ @Deprecated @Override public JsonFactory getJsonFactory() { return _parserFactory; } public TypeFactory getTypeFactory() { return _config.getTypeFactory(); } /** * @since 2.3 */ public ContextAttributes getAttributes() { return _config.getAttributes(); } /* /********************************************************** /* 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)}. *

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @SuppressWarnings("unchecked") public T readValue(JsonParser jp) throws IOException, JsonProcessingException { return (T) _bind(jp, _valueToUpdate); } /** * 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)}. *

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @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)}. *

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @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)}. *

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @Override @SuppressWarnings("unchecked") public T readValue(JsonParser jp, ResolvedType valueType) throws IOException, JsonProcessingException { return (T) withType((JavaType)valueType).readValue(jp); } /** * Type-safe overloaded method, basically alias for {@link #readValue(JsonParser, ResolvedType)}. *

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @SuppressWarnings("unchecked") public T readValue(JsonParser jp, JavaType valueType) throws IOException, JsonProcessingException { return (T) withType(valueType).readValue(jp); } /** * Convenience method that is equivalent to: *

     *   withType(valueType).readValues(jp);
     *
*

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @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);
     *
*

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @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);
     *
*

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @Override public Iterator readValues(JsonParser jp, ResolvedType valueType) throws IOException, JsonProcessingException { return readValues(jp, (JavaType) valueType); } /** * Convenience method that is equivalent to: *

     *   withType(valueType).readValues(jp);
     *
*

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ public Iterator readValues(JsonParser jp, JavaType valueType) throws IOException, JsonProcessingException { return withType(valueType).readValues(jp); } /* /********************************************************** /* TreeCodec impl /********************************************************** */ @Override public JsonNode createArrayNode() { return _config.getNodeFactory().arrayNode(); } @Override public JsonNode createObjectNode() { return _config.getNodeFactory().objectNode(); } @Override public JsonParser treeAsTokens(TreeNode n) { return new TreeTraversingParser((JsonNode) n, this); } /** * 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. *

* NOTE: this method never tries to auto-detect format, since actual * (data-format specific) parser is given. */ @SuppressWarnings("unchecked") @Override public T readTree(JsonParser jp) throws IOException, JsonProcessingException { return (T) _bindAsTree(jp); } @Override public void writeTree(JsonGenerator jgen, TreeNode rootNode) { throw new UnsupportedOperationException(); } /* /********************************************************** /* 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 { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false); } return (T) _bindAndClose(_parserFactory.createParser(src), _valueToUpdate); } /** * 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 { if (_dataFormatReaders != null) { _reportUndetectableSource(src); } return (T) _bindAndClose(_parserFactory.createParser(src), _valueToUpdate); } /** * 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 { if (_dataFormatReaders != null) { _reportUndetectableSource(src); } return (T) _bindAndClose(_parserFactory.createParser(src), _valueToUpdate); } /** * 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 { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(src, 0, src.length); } return (T) _bindAndClose(_parserFactory.createParser(src), _valueToUpdate); } /** * 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 { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(src, offset, length); } return (T) _bindAndClose(_parserFactory.createParser(src, offset, length), _valueToUpdate); } @SuppressWarnings("unchecked") public T readValue(File src) throws IOException, JsonProcessingException { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true); } return (T) _bindAndClose(_parserFactory.createParser(src), _valueToUpdate); } /** * 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 { if (_dataFormatReaders != null) { return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true); } return (T) _bindAndClose(_parserFactory.createParser(src), _valueToUpdate); } /** * 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 { if (_dataFormatReaders != null) { _reportUndetectableSource(src); } return (T) _bindAndClose(treeAsTokens(src), _valueToUpdate); } /** * 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 { if (_dataFormatReaders != null) { return _detectBindAndCloseAsTree(in); } return _bindAndCloseAsTree(_parserFactory.createParser(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 { if (_dataFormatReaders != null) { _reportUndetectableSource(r); } return _bindAndCloseAsTree(_parserFactory.createParser(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 json) throws IOException, JsonProcessingException { if (_dataFormatReaders != null) { _reportUndetectableSource(json); } return _bindAndCloseAsTree(_parserFactory.createParser(json)); } /* /********************************************************** /* Deserialization methods; reading sequence of values /********************************************************** */ /** * Method for reading sequence of Objects from parser stream. *

* Sequence can be either root-level "unwrapped" sequence (without surrounding * JSON array), or a sequence contained in a JSON Array. * In either case {@link JsonParser} must point to the first token of * the first element, OR not point to any token (in which case it is advanced * to the next token). This means, specifically, that for wrapped sequences, * parser MUST NOT point to the surrounding START_ARRAY but rather * to the token following it. */ 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(ctxt, _valueType), false, _valueToUpdate); } /** * Method for reading sequence of Objects from parser stream. *

* Sequence can be either wrapped or unwrapped root-level sequence: * wrapped means that the elements are enclosed in JSON Array; * and unwrapped that elements are directly accessed at main level. * Assumption is that iff the first token of the document is * START_ARRAY, we have a wrapped sequence; otherwise * unwrapped. For wrapped sequences, leading START_ARRAY * is skipped, so that for both cases, underlying {@link JsonParser} * will point to what is expected to be the first token of the first * element. *

* Note that the wrapped vs unwrapped logic means that it is NOT * possible to use this method for reading an unwrapped sequence * of elements written as JSON Arrays: to read such sequences, one * has to use {@link #readValues(JsonParser)}, making sure parser * points to the first token of the first element (i.e. the second * START_ARRAY which is part of the first element). */ public MappingIterator readValues(InputStream src) throws IOException, JsonProcessingException { if (_dataFormatReaders != null) { return _detectBindAndReadValues(_dataFormatReaders.findFormat(src), false); } return _bindAndReadValues(_parserFactory.createParser(src), _valueToUpdate); } /** * Overloaded version of {@link #readValue(InputStream)}. */ @SuppressWarnings("resource") public MappingIterator readValues(Reader src) throws IOException, JsonProcessingException { if (_dataFormatReaders != null) { _reportUndetectableSource(src); } JsonParser jp = _parserFactory.createParser(src); if (_schema != null) { jp.setSchema(_schema); } jp.nextToken(); DeserializationContext ctxt = createDeserializationContext(jp, _config); return new MappingIterator(_valueType, jp, ctxt, _findRootDeserializer(ctxt, _valueType), true, _valueToUpdate); } /** * Overloaded version of {@link #readValue(InputStream)}. * * @param json String that contains JSON content to parse */ @SuppressWarnings("resource") public MappingIterator readValues(String json) throws IOException, JsonProcessingException { if (_dataFormatReaders != null) { _reportUndetectableSource(json); } JsonParser jp = _parserFactory.createParser(json); if (_schema != null) { jp.setSchema(_schema); } jp.nextToken(); DeserializationContext ctxt = createDeserializationContext(jp, _config); return new MappingIterator(_valueType, jp, ctxt, _findRootDeserializer(ctxt, _valueType), true, _valueToUpdate); } /** * Overloaded version of {@link #readValue(InputStream)}. */ public MappingIterator readValues(byte[] src, int offset, int length) throws IOException, JsonProcessingException { if (_dataFormatReaders != null) { return _detectBindAndReadValues(_dataFormatReaders.findFormat(src, offset, length), false); } return _bindAndReadValues(_parserFactory.createParser(src), _valueToUpdate); } /** * Overloaded version of {@link #readValue(InputStream)}. */ public final MappingIterator readValues(byte[] src) throws IOException, JsonProcessingException { return readValues(src, 0, src.length); } /** * Overloaded version of {@link #readValue(InputStream)}. */ public MappingIterator readValues(File src) throws IOException, JsonProcessingException { if (_dataFormatReaders != null) { return _detectBindAndReadValues( _dataFormatReaders.findFormat(_inputStream(src)), false); } return _bindAndReadValues(_parserFactory.createParser(src), _valueToUpdate); } /** * Overloaded version of {@link #readValue(InputStream)}. * * @param src URL to read to access JSON content to parse. */ public MappingIterator readValues(URL src) throws IOException, JsonProcessingException { if (_dataFormatReaders != null) { return _detectBindAndReadValues( _dataFormatReaders.findFormat(_inputStream(src)), true); } return _bindAndReadValues(_parserFactory.createParser(src), _valueToUpdate); } /* /********************************************************** /* Implementation of rest of ObjectCodec methods /********************************************************** */ @Override public T treeToValue(TreeNode n, Class valueType) throws JsonProcessingException { try { return readValue(treeAsTokens(n), valueType); } catch (JsonProcessingException e) { throw e; } catch (IOException e) { // should not occur, no real i/o... throw new IllegalArgumentException(e.getMessage(), e); } } @Override public void writeValue(JsonGenerator jgen, Object value) throws IOException, JsonProcessingException { throw new UnsupportedOperationException("Not implemented for ObjectReader"); } /* /********************************************************** /* Helper methods, data-binding /********************************************************** */ /** * Actual implementation of value reading+binding operation. */ protected Object _bind(JsonParser jp, Object valueToUpdate) 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) { DeserializationContext ctxt = createDeserializationContext(jp, _config); result = _findRootDeserializer(ctxt, _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 deser = _findRootDeserializer(ctxt, _valueType); if (_unwrapRoot) { result = _unwrapAndDeserialize(jp, ctxt, _valueType, deser); } else { if (valueToUpdate == null) { result = deser.deserialize(jp, ctxt); } else { deser.deserialize(jp, ctxt, valueToUpdate); result = valueToUpdate; } } } // Need to consume the token too jp.clearCurrentToken(); return result; } protected Object _bindAndClose(JsonParser jp, Object valueToUpdate) throws IOException, JsonParseException, JsonMappingException { if (_schema != null) { jp.setSchema(_schema); } try { Object result; JsonToken t = _initForReading(jp); if (t == JsonToken.VALUE_NULL) { if (valueToUpdate == null) { DeserializationContext ctxt = createDeserializationContext(jp, _config); result = _findRootDeserializer(ctxt, _valueType).getNullValue(); } else { result = valueToUpdate; } } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { result = valueToUpdate; } else { DeserializationContext ctxt = createDeserializationContext(jp, _config); JsonDeserializer deser = _findRootDeserializer(ctxt, _valueType); if (_unwrapRoot) { result = _unwrapAndDeserialize(jp, ctxt, _valueType, deser); } else { if (valueToUpdate == null) { result = deser.deserialize(jp, ctxt); } else { deser.deserialize(jp, ctxt, valueToUpdate); result = valueToUpdate; } } } return result; } finally { try { jp.close(); } catch (IOException ioe) { } } } protected JsonNode _bindAsTree(JsonParser jp) throws IOException, JsonParseException, JsonMappingException { JsonNode result; JsonToken t = _initForReading(jp); if (t == JsonToken.VALUE_NULL || t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) { result = NullNode.instance; } else { DeserializationContext ctxt = createDeserializationContext(jp, _config); JsonDeserializer deser = _findRootDeserializer(ctxt, JSON_NODE_TYPE); if (_unwrapRoot) { result = (JsonNode) _unwrapAndDeserialize(jp, ctxt, JSON_NODE_TYPE, deser); } else { result = (JsonNode) deser.deserialize(jp, ctxt); } } // Need to consume the token too jp.clearCurrentToken(); return result; } protected JsonNode _bindAndCloseAsTree(JsonParser jp) throws IOException, JsonParseException, JsonMappingException { if (_schema != null) { jp.setSchema(_schema); } try { return _bindAsTree(jp); } finally { try { jp.close(); } catch (IOException ioe) { } } } /** * @since 2.1 */ protected MappingIterator _bindAndReadValues(JsonParser p, Object valueToUpdate) throws IOException, JsonProcessingException { if (_schema != null) { p.setSchema(_schema); } p.nextToken(); DeserializationContext ctxt = createDeserializationContext(p, _config); return new MappingIterator(_valueType, p, ctxt, _findRootDeserializer(ctxt, _valueType), true, _valueToUpdate); } protected static JsonToken _initForReading(JsonParser jp) throws IOException, JsonParseException, JsonMappingException { /* First: must point to a token; if not pointing to one, advance. * This occurs before first read from JsonParser, as well as * after clearing of current token. */ JsonToken t = jp.getCurrentToken(); if (t == null) { // and then we must get something... t = jp.nextToken(); if (t == null) { /* [JACKSON-546] Throw mapping exception, since it's failure to map, * not an actual parsing problem */ throw JsonMappingException.from(jp, "No content to map due to end-of-input"); } } return t; } /** * Method called to locate deserializer for the passed root-level value. */ protected JsonDeserializer _findRootDeserializer(DeserializationContext ctxt, JavaType valueType) throws JsonMappingException { if (_rootDeserializer != null) { return _rootDeserializer; } // Sanity check: must have actual type... if (valueType == null) { throw new JsonMappingException("No value type configured for ObjectReader"); } // First: have we already seen it? JsonDeserializer deser = _rootDeserializers.get(valueType); if (deser != null) { return deser; } // Nope: need to ask provider to resolve it deser = ctxt.findRootValueDeserializer(valueType); if (deser == null) { // can this happen? throw new JsonMappingException("Can not find a deserializer for type "+valueType); } _rootDeserializers.put(valueType, deser); return deser; } /** * Method called to locate deserializer ahead of time, if permitted * by configuration. Method also is NOT to throw an exception if * access fails. */ protected JsonDeserializer _prefetchRootDeserializer( DeserializationConfig config, JavaType valueType) { if (valueType == null || !_config.isEnabled(DeserializationFeature.EAGER_DESERIALIZER_FETCH)) { return null; } // already cached? JsonDeserializer deser = _rootDeserializers.get(valueType); if (deser == null) { try { // If not, need to resolve; for which we need a temporary context as well: DeserializationContext ctxt = createDeserializationContext(null, _config); deser = ctxt.findRootValueDeserializer(valueType); if (deser != null) { _rootDeserializers.put(valueType, deser); } return deser; } catch (JsonProcessingException e) { // need to swallow? } } return deser; } protected Object _unwrapAndDeserialize(JsonParser jp, DeserializationContext ctxt, JavaType rootType, JsonDeserializer deser) throws IOException, JsonParseException, JsonMappingException { String expName = _config.getRootName(); if (expName == null) { PropertyName pname = _rootNames.findRootName(rootType, _config); expName = pname.getSimpleName(); } if (jp.getCurrentToken() != JsonToken.START_OBJECT) { throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '" +expName+"'), but "+jp.getCurrentToken()); } if (jp.nextToken() != JsonToken.FIELD_NAME) { throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '" +expName+"'), but "+jp.getCurrentToken()); } String actualName = jp.getCurrentName(); if (!expName.equals(actualName)) { throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('" +expName+"') for type "+rootType); } // ok, then move to value itself.... jp.nextToken(); Object result; if (_valueToUpdate == null) { result = deser.deserialize(jp, ctxt); } else { deser.deserialize(jp, ctxt, _valueToUpdate); result = _valueToUpdate; } // and last, verify that we now get matching END_OBJECT if (jp.nextToken() != JsonToken.END_OBJECT) { throw JsonMappingException.from(jp, "Current token not END_OBJECT (to match wrapper object with root name '" +expName+"'), but "+jp.getCurrentToken()); } return result; } /* /********************************************************** /* Internal methods, format auto-detection (since 2.1) /********************************************************** */ @SuppressWarnings("resource") protected Object _detectBindAndClose(byte[] src, int offset, int length) throws IOException { DataFormatReaders.Match match = _dataFormatReaders.findFormat(src, offset, length); if (!match.hasMatch()) { _reportUnkownFormat(_dataFormatReaders, match); } JsonParser jp = match.createParserWithMatch(); return match.getReader()._bindAndClose(jp, _valueToUpdate); } @SuppressWarnings("resource") protected Object _detectBindAndClose(DataFormatReaders.Match match, boolean forceClosing) throws IOException { if (!match.hasMatch()) { _reportUnkownFormat(_dataFormatReaders, match); } JsonParser p = match.createParserWithMatch(); // One more thing: we Own the input stream now; and while it's // not super clean way to do it, we must ensure closure so: if (forceClosing) { p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); } // important: use matching ObjectReader (may not be 'this') return match.getReader()._bindAndClose(p, _valueToUpdate); } @SuppressWarnings("resource") protected MappingIterator _detectBindAndReadValues(DataFormatReaders.Match match, boolean forceClosing) throws IOException, JsonProcessingException { if (!match.hasMatch()) { _reportUnkownFormat(_dataFormatReaders, match); } JsonParser p = match.createParserWithMatch(); // One more thing: we Own the input stream now; and while it's // not super clean way to do it, we must ensure closure so: if (forceClosing) { p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); } // important: use matching ObjectReader (may not be 'this') return match.getReader()._bindAndReadValues(p, _valueToUpdate); } @SuppressWarnings("resource") protected JsonNode _detectBindAndCloseAsTree(InputStream in) throws IOException { DataFormatReaders.Match match = _dataFormatReaders.findFormat(in); if (!match.hasMatch()) { _reportUnkownFormat(_dataFormatReaders, match); } JsonParser p = match.createParserWithMatch(); p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); return match.getReader()._bindAndCloseAsTree(p); } /** * Method called to indicate that format detection failed to detect format * of given input */ protected void _reportUnkownFormat(DataFormatReaders detector, DataFormatReaders.Match match) throws JsonProcessingException { throw new JsonParseException("Can not detect format from input, does not look like any of detectable formats " +detector.toString(), JsonLocation.NA); } /* /********************************************************** /* Internal methods, other /********************************************************** */ /** * @since 2.2 */ protected void _verifySchemaType(FormatSchema schema) { if (schema != null) { if (!_parserFactory.canUseSchema(schema)) { throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName() +" for format "+_parserFactory.getFormatName()); } } } /** * Internal helper method called to create an instance of {@link DeserializationContext} * for deserializing a single root value. * Can be overridden if a custom context is needed. */ protected DefaultDeserializationContext createDeserializationContext(JsonParser jp, DeserializationConfig cfg) { // 04-Jan-2010, tatu: we do actually need the provider too... (for polymorphic deser) return _context.createInstance(cfg, jp, _injectableValues); } protected ObjectReader _with(DeserializationConfig newConfig) { if (newConfig == _config) { return this; } if (_dataFormatReaders != null) { return new ObjectReader(this, newConfig) .withFormatDetection(_dataFormatReaders.with(newConfig)); } return new ObjectReader(this, newConfig); } protected void _reportUndetectableSource(Object src) throws JsonProcessingException { throw new JsonParseException("Can not use source of type " +src.getClass().getName()+" with format auto-detection: must be byte- not char-based", JsonLocation.NA); } protected InputStream _inputStream(URL src) throws IOException { return src.openStream(); } protected InputStream _inputStream(File f) throws IOException { return new FileInputStream(f); } }