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

org.codehaus.jackson.map.ObjectReader 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;

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 deser = _findRootDeserializer(_config, _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) 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) { result = _findRootDeserializer(_config, _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(_config, _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(_config, 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) { } } } 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-99] Should throw EOFException? throw new EOFException("No content to map to Object due to end of input"); } } return t; } /** * Method called to locate deserializer for the passed root-level value. */ protected JsonDeserializer _findRootDeserializer(DeserializationConfig cfg, JavaType valueType) throws JsonMappingException { // 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 = _provider.findTypedValueDeserializer(cfg, valueType, null); if (deser == null) { // can this happen? throw new JsonMappingException("Can not find a deserializer for type "+valueType); } _rootDeserializers.put(valueType, deser); return deser; } protected DeserializationContext _createDeserializationContext(JsonParser jp, DeserializationConfig cfg) { // 04-Jan-2010, tatu: we do actually need the provider too... (for polymorphic deser) return new StdDeserializationContext(cfg, jp, _provider, _injectableValues); } protected Object _unwrapAndDeserialize(JsonParser jp, DeserializationContext ctxt, JavaType rootType, JsonDeserializer deser) throws IOException, JsonParseException, JsonMappingException { SerializedString rootName = _provider.findExpectedRootName(ctxt.getConfig(), rootType); if (jp.getCurrentToken() != JsonToken.START_OBJECT) { throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '" +rootName+"'), but "+jp.getCurrentToken()); } if (jp.nextToken() != JsonToken.FIELD_NAME) { throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '" +rootName+"'), but "+jp.getCurrentToken()); } String actualName = jp.getCurrentName(); if (!rootName.getValue().equals(actualName)) { throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"+rootName +"') 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 '" +rootName+"'), but "+jp.getCurrentToken()); } return result; } /* /********************************************************** /* Implementation of rest of ObjectCodec methods /********************************************************** */ @Override public JsonNode createArrayNode() { return _config.getNodeFactory().arrayNode(); } @Override public JsonNode createObjectNode() { return _config.getNodeFactory().objectNode(); } @Override public JsonParser treeAsTokens(JsonNode n) { return new TreeTraversingParser(n, this); } @Override public T treeToValue(JsonNode n, Class valueType) throws IOException, JsonProcessingException { return readValue(treeAsTokens(n), valueType); } /** * NOTE: NOT implemented for {@link ObjectReader}. */ @Override public void writeTree(JsonGenerator jgen, JsonNode rootNode) throws IOException, JsonProcessingException { throw new UnsupportedOperationException("Not implemented for ObjectReader"); } @Override public void writeValue(JsonGenerator jgen, Object value) throws IOException, JsonProcessingException { throw new UnsupportedOperationException("Not implemented for ObjectReader"); } }