blob: b57115aefa8a8dedd62286a457c2c4f3f662b922 [file] [log] [blame]
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<JavaType, JsonDeserializer<Object>> _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.
*<p>
* 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.
*<p>
* 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.
*<p>
* 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.
*<p>
* 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.
*<p>
* 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.
*<p>
* 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.
*<p>
* 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.
*<p>
* 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> 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> T readValue(JsonParser jp, Class<T> 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> 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> 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.
*<p>
* 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:
*<pre>
* withType(valueType).readValues(jp);
*</pre>
*/
@Override
public <T> Iterator<T> readValues(JsonParser jp, Class<T> valueType)
throws IOException, JsonProcessingException {
return withType(valueType).readValues(jp);
}
/**
* Convenience method that is equivalent to:
*<pre>
* withType(valueTypeRef).readValues(jp);
*</pre>
*/
@Override
public <T> Iterator<T> readValues(JsonParser jp, TypeReference<?> valueTypeRef)
throws IOException, JsonProcessingException {
return withType(valueTypeRef).readValues(jp);
}
/**
* Convenience method that is equivalent to:
*<pre>
* withType(valueType).readValues(jp);
*</pre>
*/
@Override
public <T> Iterator<T> 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> 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> 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> 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> 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> T readValue(byte[] src, int offset, int length)
throws IOException, JsonProcessingException
{
return (T) _bindAndClose(_jsonFactory.createJsonParser(src, offset, length));
}
@SuppressWarnings("unchecked")
public <T> 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> 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:
*<pre>
* objectReader.readValue(src.traverse())
*</pre>
*/
@SuppressWarnings("unchecked")
public <T> 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.
*<p>
* 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.
*<p>
* 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.
*<p>
* 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 <T> MappingIterator<T> readValues(JsonParser jp)
throws IOException, JsonProcessingException
{
DeserializationContext ctxt = _createDeserializationContext(jp, _config);
// false -> do not close as caller gave parser instance
return new MappingIterator<T>(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType),
false, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public <T> MappingIterator<T> 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<T>(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType),
true, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public <T> MappingIterator<T> 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<T>(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public <T> MappingIterator<T> 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<T>(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public <T> MappingIterator<T> 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<T>(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/**
* Since 1.9.3
*/
public final <T> MappingIterator<T> 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 <T> MappingIterator<T> 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<T>(_valueType, jp, ctxt,
_findRootDeserializer(_config, _valueType), true, _valueToUpdate);
}
/**
* Method for reading sequence of Objects from parser stream.
*
* @since 1.8
*/
public <T> MappingIterator<T> 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<T>(_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<Object> 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<Object> 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<Object> 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<Object> _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<Object> 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<Object> 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> T treeToValue(JsonNode n, Class<T> 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");
}
}