blob: dc8776b1c2596c4a17bfbea6c1fafc76948935e1 [file] [log] [blame]
package com.fasterxml.jackson.databind.deser;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.impl.*;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.databind.util.TokenBuffer;
/**
* Deserializer class that can deserialize instances of
* arbitrary bean objects, usually from JSON Object structs,
*/
public class BeanDeserializer
extends BeanDeserializerBase
implements java.io.Serializable
{
/* TODOs for future versions:
*
* For 2.9?
*
* - New method in JsonDeserializer (deserializeNext()) to allow use of more
* efficient 'nextXxx()' method `JsonParser` provides.
*
* Also: need to ensure efficient impl of those methods for Smile, CBOR
* at least (in addition to JSON)
*/
private static final long serialVersionUID = 1L;
/**
* Lazily constructed exception used as root cause if reporting problem
* with creator method that returns <code>null</code> (which is not allowed)
*
* @since 2.8
*/
protected transient Exception _nullFromCreator;
/**
* State marker we need in order to avoid infinite recursion for some cases
* (not very clean, alas, but has to do for now)
*
* @since 2.9
*/
private volatile transient NameTransformer _currentlyTransforming;
/*
/**********************************************************
/* Life-cycle, construction, initialization
/**********************************************************
*/
/**
* Constructor used by {@link BeanDeserializerBuilder}.
*/
public BeanDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc,
BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
HashSet<String> ignorableProps, boolean ignoreAllUnknown,
boolean hasViews)
{
super(builder, beanDesc, properties, backRefs,
ignorableProps, ignoreAllUnknown, hasViews);
}
/**
* Copy-constructor that can be used by sub-classes to allow
* copy-on-write style copying of settings of an existing instance.
*/
protected BeanDeserializer(BeanDeserializerBase src) {
super(src, src._ignoreAllUnknown);
}
protected BeanDeserializer(BeanDeserializerBase src, boolean ignoreAllUnknown) {
super(src, ignoreAllUnknown);
}
protected BeanDeserializer(BeanDeserializerBase src, NameTransformer unwrapper) {
super(src, unwrapper);
}
public BeanDeserializer(BeanDeserializerBase src, ObjectIdReader oir) {
super(src, oir);
}
public BeanDeserializer(BeanDeserializerBase src, Set<String> ignorableProps) {
super(src, ignorableProps);
}
public BeanDeserializer(BeanDeserializerBase src, BeanPropertyMap props) {
super(src, props);
}
@Override
public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer transformer)
{
// bit kludgy but we don't want to accidentally change type; sub-classes
// MUST override this method to support unwrapped properties...
if (getClass() != BeanDeserializer.class) {
return this;
}
// 25-Mar-2017, tatu: Not clean at all, but for [databind#383] we do need
// to keep track of accidental recursion...
if (_currentlyTransforming == transformer) {
return this;
}
_currentlyTransforming = transformer;
try {
return new BeanDeserializer(this, transformer);
} finally { _currentlyTransforming = null; }
}
@Override
public BeanDeserializer withObjectIdReader(ObjectIdReader oir) {
return new BeanDeserializer(this, oir);
}
@Override
public BeanDeserializer withIgnorableProperties(Set<String> ignorableProps) {
return new BeanDeserializer(this, ignorableProps);
}
@Override
public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
return new BeanDeserializer(this, props);
}
@Override
protected BeanDeserializerBase asArrayDeserializer() {
SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder();
return new BeanAsArrayDeserializer(this, props);
}
/*
/**********************************************************
/* JsonDeserializer implementation
/**********************************************************
*/
/**
* Main deserialization method for bean-based objects (POJOs).
*/
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
// common case first
if (p.isExpectedStartObjectToken()) {
if (_vanillaProcessing) {
return vanillaDeserialize(p, ctxt, p.nextToken());
}
// 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is
// what it is, including "expected behavior".
p.nextToken();
if (_objectIdReader != null) {
return deserializeWithObjectId(p, ctxt);
}
return deserializeFromObject(p, ctxt);
}
return _deserializeOther(p, ctxt, p.getCurrentToken());
}
protected final Object _deserializeOther(JsonParser p, DeserializationContext ctxt,
JsonToken t) throws IOException
{
// and then others, generally requiring use of @JsonCreator
if (t != null) {
switch (t) {
case VALUE_STRING:
return deserializeFromString(p, ctxt);
case VALUE_NUMBER_INT:
return deserializeFromNumber(p, ctxt);
case VALUE_NUMBER_FLOAT:
return deserializeFromDouble(p, ctxt);
case VALUE_EMBEDDED_OBJECT:
return deserializeFromEmbedded(p, ctxt);
case VALUE_TRUE:
case VALUE_FALSE:
return deserializeFromBoolean(p, ctxt);
case VALUE_NULL:
return deserializeFromNull(p, ctxt);
case START_ARRAY:
// these only work if there's a (delegating) creator...
return deserializeFromArray(p, ctxt);
case FIELD_NAME:
case END_OBJECT: // added to resolve [JACKSON-319], possible related issues
if (_vanillaProcessing) {
return vanillaDeserialize(p, ctxt, t);
}
if (_objectIdReader != null) {
return deserializeWithObjectId(p, ctxt);
}
return deserializeFromObject(p, ctxt);
default:
}
}
return ctxt.handleUnexpectedToken(getValueType(ctxt), p);
}
@Deprecated // since 2.8; remove unless getting used
protected Object _missingToken(JsonParser p, DeserializationContext ctxt) throws IOException {
throw ctxt.endOfInputException(handledType());
}
/**
* Secondary deserialization method, called in cases where POJO
* instance is created as part of deserialization, potentially
* after collecting some or all of the properties to set.
*/
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean) throws IOException
{
// [databind#631]: Assign current value, to be accessible by custom serializers
p.setCurrentValue(bean);
if (_injectables != null) {
injectValues(ctxt, bean);
}
if (_unwrappedPropertyHandler != null) {
return deserializeWithUnwrapped(p, ctxt, bean);
}
if (_externalTypeIdHandler != null) {
return deserializeWithExternalTypeId(p, ctxt, bean);
}
String propName;
// 23-Mar-2010, tatu: In some cases, we start with full JSON object too...
if (p.isExpectedStartObjectToken()) {
propName = p.nextFieldName();
if (propName == null) {
return bean;
}
} else {
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
propName = p.getCurrentName();
} else {
return bean;
}
}
if (_needViewProcesing) {
Class<?> view = ctxt.getActiveView();
if (view != null) {
return deserializeWithView(p, ctxt, bean, view);
}
}
do {
p.nextToken();
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
try {
prop.deserializeAndSet(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
handleUnknownVanilla(p, ctxt, bean, propName);
} while ((propName = p.nextFieldName()) != null);
return bean;
}
/*
/**********************************************************
/* Concrete deserialization methods
/**********************************************************
*/
/**
* Streamlined version that is only used when no "special"
* features are enabled.
*/
private final Object vanillaDeserialize(JsonParser p,
DeserializationContext ctxt, JsonToken t)
throws IOException
{
final Object bean = _valueInstantiator.createUsingDefault(ctxt);
// [databind#631]: Assign current value, to be accessible by custom serializers
p.setCurrentValue(bean);
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
String propName = p.getCurrentName();
do {
p.nextToken();
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
try {
prop.deserializeAndSet(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
handleUnknownVanilla(p, ctxt, bean, propName);
} while ((propName = p.nextFieldName()) != null);
}
return bean;
}
/**
* General version used when handling needs more advanced features.
*/
@Override
public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException
{
/* 09-Dec-2014, tatu: As per [databind#622], we need to allow Object Id references
* to come in as JSON Objects as well; but for now assume they will
* be simple, single-property references, which means that we can
* recognize them without having to buffer anything.
* Once again, if we must, we can do more complex handling with buffering,
* but let's only do that if and when that becomes necessary.
*/
if ((_objectIdReader != null) && _objectIdReader.maySerializeAsObject()) {
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)
&& _objectIdReader.isValidReferencePropertyName(p.getCurrentName(), p)) {
return deserializeFromObjectId(p, ctxt);
}
}
if (_nonStandardCreation) {
if (_unwrappedPropertyHandler != null) {
return deserializeWithUnwrapped(p, ctxt);
}
if (_externalTypeIdHandler != null) {
return deserializeWithExternalTypeId(p, ctxt);
}
Object bean = deserializeFromObjectUsingNonDefault(p, ctxt);
if (_injectables != null) {
injectValues(ctxt, bean);
}
/* 27-May-2014, tatu: I don't think view processing would work
* at this point, so commenting it out; but leaving in place
* just in case I forgot something fundamental...
*/
/*
if (_needViewProcesing) {
Class<?> view = ctxt.getActiveView();
if (view != null) {
return deserializeWithView(p, ctxt, bean, view);
}
}
*/
return bean;
}
final Object bean = _valueInstantiator.createUsingDefault(ctxt);
// [databind#631]: Assign current value, to be accessible by custom deserializers
p.setCurrentValue(bean);
if (p.canReadObjectId()) {
Object id = p.getObjectId();
if (id != null) {
_handleTypedObjectId(p, ctxt, bean, id);
}
}
if (_injectables != null) {
injectValues(ctxt, bean);
}
if (_needViewProcesing) {
Class<?> view = ctxt.getActiveView();
if (view != null) {
return deserializeWithView(p, ctxt, bean, view);
}
}
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
String propName = p.getCurrentName();
do {
p.nextToken();
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
try {
prop.deserializeAndSet(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
handleUnknownVanilla(p, ctxt, bean, propName);
} while ((propName = p.nextFieldName()) != null);
}
return bean;
}
/**
* Method called to deserialize bean using "property-based creator":
* this means that a non-default constructor or factory method is
* called, and then possibly other setters. The trick is that
* values for creator method need to be buffered, first; and
* due to non-guaranteed ordering possibly some other properties
* as well.
*/
@Override
@SuppressWarnings("resource")
protected Object _deserializeUsingPropertyBased(final JsonParser p, final DeserializationContext ctxt)
throws IOException
{
final PropertyBasedCreator creator = _propertyBasedCreator;
PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader);
TokenBuffer unknown = null;
final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
JsonToken t = p.getCurrentToken();
List<BeanReferring> referrings = null;
for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String propName = p.getCurrentName();
p.nextToken(); // to point to value
// Object Id property?
if (buffer.readIdProperty(propName)) {
continue;
}
// creator property?
SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
if (creatorProp != null) {
// Last creator property to set?
Object value;
if ((activeView != null) && !creatorProp.visibleInView(activeView)) {
p.skipChildren();
continue;
}
value = _deserializeWithErrorWrapping(p, ctxt, creatorProp);
if (buffer.assignParameter(creatorProp, value)) {
p.nextToken(); // to move to following FIELD_NAME/END_OBJECT
Object bean;
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
bean = wrapInstantiationProblem(e, ctxt);
}
if (bean == null) {
return ctxt.handleInstantiationProblem(handledType(), null,
_creatorReturnedNullException());
}
// [databind#631]: Assign current value, to be accessible by custom serializers
p.setCurrentValue(bean);
// polymorphic?
if (bean.getClass() != _beanType.getRawClass()) {
return handlePolymorphic(p, ctxt, bean, unknown);
}
if (unknown != null) { // nope, just extra unknown stuff...
bean = handleUnknownProperties(ctxt, bean, unknown);
}
// or just clean?
return deserialize(p, ctxt, bean);
}
continue;
}
// regular property? needs buffering
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) {
try {
buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop));
} catch (UnresolvedForwardReference reference) {
// 14-Jun-2016, tatu: As per [databind#1261], looks like we need additional
// handling of forward references here. Not exactly sure why existing
// facilities did not cover, but this does appear to solve the problem
BeanReferring referring = handleUnresolvedReference(ctxt,
prop, buffer, reference);
if (referrings == null) {
referrings = new ArrayList<BeanReferring>();
}
referrings.add(referring);
}
continue;
}
// Things marked as ignorable should not be passed to any setter
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
}
// "any property"?
if (_anySetter != null) {
try {
buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt));
} catch (Exception e) {
wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
}
continue;
}
// Ok then, let's collect the whole field; name and value
if (unknown == null) {
unknown = new TokenBuffer(p, ctxt);
}
unknown.writeFieldName(propName);
unknown.copyCurrentStructure(p);
}
// We hit END_OBJECT, so:
Object bean;
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
wrapInstantiationProblem(e, ctxt);
bean = null; // never gets here
}
if (referrings != null) {
for (BeanReferring referring : referrings) {
referring.setBean(bean);
}
}
if (unknown != null) {
// polymorphic?
if (bean.getClass() != _beanType.getRawClass()) {
return handlePolymorphic(null, ctxt, bean, unknown);
}
// no, just some extra unknown properties
return handleUnknownProperties(ctxt, bean, unknown);
}
return bean;
}
/**
* @since 2.8
*/
private BeanReferring handleUnresolvedReference(DeserializationContext ctxt,
SettableBeanProperty prop, PropertyValueBuffer buffer,
UnresolvedForwardReference reference)
throws JsonMappingException
{
BeanReferring referring = new BeanReferring(ctxt, reference,
prop.getType(), buffer, prop);
reference.getRoid().appendReferring(referring);
return referring;
}
protected final Object _deserializeWithErrorWrapping(JsonParser p,
DeserializationContext ctxt, SettableBeanProperty prop)
throws IOException
{
try {
return prop.deserialize(p, ctxt);
} catch (Exception e) {
wrapAndThrow(e, _beanType.getRawClass(), prop.getName(), ctxt);
// never gets here, unless caller declines to throw an exception
return null;
}
}
/**
* Helper method called for rare case of pointing to {@link JsonToken#VALUE_NULL}
* token. While this is most often an erroneous condition, there is one specific
* case with XML handling where polymorphic type with no properties is exposed
* as such, and should be handled same as empty Object.
*
* @since 2.7
*/
protected Object deserializeFromNull(JsonParser p, DeserializationContext ctxt)
throws IOException
{
// 17-Dec-2015, tatu: Highly specialized case, mainly to support polymorphic
// "empty" POJOs deserialized from XML, where empty XML tag synthesizes a
// `VALUE_NULL` token.
if (p.requiresCustomCodec()) { // not only XML module, but mostly it...
@SuppressWarnings("resource")
TokenBuffer tb = new TokenBuffer(p, ctxt);
tb.writeEndObject();
JsonParser p2 = tb.asParser(p);
p2.nextToken(); // to point to END_OBJECT
// note: don't have ObjectId to consider at this point, so:
Object ob = _vanillaProcessing ? vanillaDeserialize(p2, ctxt, JsonToken.END_OBJECT)
: deserializeFromObject(p2, ctxt);
p2.close();
return ob;
}
return ctxt.handleUnexpectedToken(getValueType(ctxt), p);
}
/*
/**********************************************************
/* Deserializing when we have to consider an active View
/**********************************************************
*/
protected final Object deserializeWithView(JsonParser p, DeserializationContext ctxt,
Object bean, Class<?> activeView)
throws IOException
{
if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
String propName = p.getCurrentName();
do {
p.nextToken();
// TODO: 06-Jan-2015, tatu: try streamlining call sequences here as well
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) {
if (!prop.visibleInView(activeView)) {
p.skipChildren();
continue;
}
try {
prop.deserializeAndSet(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
handleUnknownVanilla(p, ctxt, bean, propName);
} while ((propName = p.nextFieldName()) != null);
}
return bean;
}
/*
/**********************************************************
/* Handling for cases where we have "unwrapped" values
/**********************************************************
*/
/**
* Method called when there are declared "unwrapped" properties
* which need special handling
*/
@SuppressWarnings("resource")
protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt)
throws IOException
{
if (_delegateDeserializer != null) {
return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(p, ctxt));
}
if (_propertyBasedCreator != null) {
return deserializeUsingPropertyBasedWithUnwrapped(p, ctxt);
}
TokenBuffer tokens = new TokenBuffer(p, ctxt);
tokens.writeStartObject();
final Object bean = _valueInstantiator.createUsingDefault(ctxt);
// [databind#631]: Assign current value, to be accessible by custom serializers
p.setCurrentValue(bean);
if (_injectables != null) {
injectValues(ctxt, bean);
}
final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
String propName = p.hasTokenId(JsonTokenId.ID_FIELD_NAME) ? p.getCurrentName() : null;
for (; propName != null; propName = p.nextFieldName()) {
p.nextToken();
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
if ((activeView != null) && !prop.visibleInView(activeView)) {
p.skipChildren();
continue;
}
try {
prop.deserializeAndSet(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
// Things marked as ignorable should not be passed to any setter
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
// 29-Nov-2016, tatu: probably should try to avoid sending content
// both to any setter AND buffer... but, for now, the only thing
// we can do.
// how about any setter? We'll get copies but...
if (_anySetter == null) {
// but... others should be passed to unwrapped property deserializers
tokens.writeFieldName(propName);
tokens.copyCurrentStructure(p);
continue;
}
// Need to copy to a separate buffer first
TokenBuffer b2 = TokenBuffer.asCopyOfValue(p);
tokens.writeFieldName(propName);
tokens.append(b2);
try {
_anySetter.deserializeAndSet(b2.asParserOnFirstToken(), ctxt, bean, propName);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
}
tokens.writeEndObject();
_unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
return bean;
}
@SuppressWarnings("resource")
protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt,
Object bean)
throws IOException
{
JsonToken t = p.getCurrentToken();
if (t == JsonToken.START_OBJECT) {
t = p.nextToken();
}
TokenBuffer tokens = new TokenBuffer(p, ctxt);
tokens.writeStartObject();
final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String propName = p.getCurrentName();
SettableBeanProperty prop = _beanProperties.find(propName);
p.nextToken();
if (prop != null) { // normal case
if (activeView != null && !prop.visibleInView(activeView)) {
p.skipChildren();
continue;
}
try {
prop.deserializeAndSet(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
// 29-Nov-2016, tatu: probably should try to avoid sending content
// both to any setter AND buffer... but, for now, the only thing
// we can do.
// how about any setter? We'll get copies but...
if (_anySetter == null) {
// but... others should be passed to unwrapped property deserializers
tokens.writeFieldName(propName);
tokens.copyCurrentStructure(p);
} else {
// Need to copy to a separate buffer first
TokenBuffer b2 = TokenBuffer.asCopyOfValue(p);
tokens.writeFieldName(propName);
tokens.append(b2);
try {
_anySetter.deserializeAndSet(b2.asParserOnFirstToken(), ctxt, bean, propName);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
}
tokens.writeEndObject();
_unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
return bean;
}
@SuppressWarnings("resource")
protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, DeserializationContext ctxt)
throws IOException
{
// 01-Dec-2016, tatu: Note: This IS legal to call, but only when unwrapped
// value itself is NOT passed via `CreatorProperty` (which isn't supported).
// Ok however to pass via setter or field.
final PropertyBasedCreator creator = _propertyBasedCreator;
PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader);
TokenBuffer tokens = new TokenBuffer(p, ctxt);
tokens.writeStartObject();
JsonToken t = p.getCurrentToken();
for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String propName = p.getCurrentName();
p.nextToken(); // to point to value
// creator property?
SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
if (creatorProp != null) {
// Last creator property to set?
if (buffer.assignParameter(creatorProp,
_deserializeWithErrorWrapping(p, ctxt, creatorProp))) {
t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT
Object bean;
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
bean = wrapInstantiationProblem(e, ctxt);
}
// [databind#631]: Assign current value, to be accessible by custom serializers
p.setCurrentValue(bean);
// if so, need to copy all remaining tokens into buffer
while (t == JsonToken.FIELD_NAME) {
// NOTE: do NOT skip name as it needs to be copied; `copyCurrentStructure` does that
tokens.copyCurrentStructure(p);
t = p.nextToken();
}
// 28-Aug-2018, tatu: Let's add sanity check here, easier to catch off-by-some
// problems if we maintain invariants
if (t != JsonToken.END_OBJECT) {
ctxt.reportWrongTokenException(this, JsonToken.END_OBJECT,
"Attempted to unwrap '%s' value",
handledType().getName());
}
tokens.writeEndObject();
if (bean.getClass() != _beanType.getRawClass()) {
// !!! 08-Jul-2011, tatu: Could probably support; but for now
// it's too complicated, so bail out
ctxt.reportInputMismatch(creatorProp,
"Cannot create polymorphic instances with unwrapped values");
return null;
}
return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
}
continue;
}
// Object Id property?
if (buffer.readIdProperty(propName)) {
continue;
}
// regular property? needs buffering
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) {
buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop));
continue;
}
// Things marked as ignorable should not be passed to any setter
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
}
// 29-Nov-2016, tatu: probably should try to avoid sending content
// both to any setter AND buffer... but, for now, the only thing
// we can do.
// how about any setter? We'll get copies but...
if (_anySetter == null) {
// but... others should be passed to unwrapped property deserializers
tokens.writeFieldName(propName);
tokens.copyCurrentStructure(p);
} else {
// Need to copy to a separate buffer first
TokenBuffer b2 = TokenBuffer.asCopyOfValue(p);
tokens.writeFieldName(propName);
tokens.append(b2);
try {
buffer.bufferAnyProperty(_anySetter, propName,
_anySetter.deserialize(b2.asParserOnFirstToken(), ctxt));
} catch (Exception e) {
wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
}
continue;
}
}
// We hit END_OBJECT, so:
Object bean;
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
wrapInstantiationProblem(e, ctxt);
return null; // never gets here
}
return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens);
}
/*
/**********************************************************
/* Handling for cases where we have property/-ies with
/* external type id
/**********************************************************
*/
protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationContext ctxt)
throws IOException
{
if (_propertyBasedCreator != null) {
return deserializeUsingPropertyBasedWithExternalTypeId(p, ctxt);
}
if (_delegateDeserializer != null) {
/* 24-Nov-2015, tatu: Use of delegating creator needs to have precedence, and basically
* external type id handling just has to be ignored, as they would relate to target
* type and not delegate type. Whether this works as expected is another story, but
* there's no other way to really mix these conflicting features.
*/
return _valueInstantiator.createUsingDelegate(ctxt,
_delegateDeserializer.deserialize(p, ctxt));
}
return deserializeWithExternalTypeId(p, ctxt, _valueInstantiator.createUsingDefault(ctxt));
}
protected Object deserializeWithExternalTypeId(JsonParser p, DeserializationContext ctxt,
Object bean)
throws IOException
{
final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
final ExternalTypeHandler ext = _externalTypeIdHandler.start();
for (JsonToken t = p.getCurrentToken(); t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String propName = p.getCurrentName();
t = p.nextToken();
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
// [JACKSON-831]: may have property AND be used as external type id:
if (t.isScalarValue()) {
ext.handleTypePropertyValue(p, ctxt, propName, bean);
}
if (activeView != null && !prop.visibleInView(activeView)) {
p.skipChildren();
continue;
}
try {
prop.deserializeAndSet(p, ctxt, bean);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
// ignorable things should be ignored
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
handleIgnoredProperty(p, ctxt, bean, propName);
continue;
}
// but others are likely to be part of external type id thingy...
if (ext.handlePropertyValue(p, ctxt, propName, bean)) {
continue;
}
// if not, the usual fallback handling:
if (_anySetter != null) {
try {
_anySetter.deserializeAndSet(p, ctxt, bean, propName);
} catch (Exception e) {
wrapAndThrow(e, bean, propName, ctxt);
}
continue;
}
// Unknown: let's call handler method
handleUnknownProperty(p, ctxt, bean, propName);
}
// and when we get this far, let's try finalizing the deal:
return ext.complete(p, ctxt, bean);
}
@SuppressWarnings("resource")
protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, DeserializationContext ctxt)
throws IOException
{
final ExternalTypeHandler ext = _externalTypeIdHandler.start();
final PropertyBasedCreator creator = _propertyBasedCreator;
PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader);
TokenBuffer tokens = new TokenBuffer(p, ctxt);
tokens.writeStartObject();
JsonToken t = p.getCurrentToken();
for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String propName = p.getCurrentName();
p.nextToken(); // to point to value
// creator property?
SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
if (creatorProp != null) {
// first: let's check to see if this might be part of value with external type id:
// 11-Sep-2015, tatu: Important; do NOT pass buffer as last arg, but null,
// since it is not the bean
if (ext.handlePropertyValue(p, ctxt, propName, null)) {
;
} else {
// Last creator property to set?
if (buffer.assignParameter(creatorProp, _deserializeWithErrorWrapping(p, ctxt, creatorProp))) {
t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT
Object bean;
try {
bean = creator.build(ctxt, buffer);
} catch (Exception e) {
wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
continue; // never gets here
}
// if so, need to copy all remaining tokens into buffer
while (t == JsonToken.FIELD_NAME) {
p.nextToken(); // to skip name
tokens.copyCurrentStructure(p);
t = p.nextToken();
}
if (bean.getClass() != _beanType.getRawClass()) {
// !!! 08-Jul-2011, tatu: Could theoretically support; but for now
// it's too complicated, so bail out
return ctxt.reportBadDefinition(_beanType, String.format(
"Cannot create polymorphic instances with external type ids (%s -> %s)",
_beanType, bean.getClass()));
}
return ext.complete(p, ctxt, bean);
}
}
continue;
}
// Object Id property?
if (buffer.readIdProperty(propName)) {
continue;
}
// regular property? needs buffering
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) {
buffer.bufferProperty(prop, prop.deserialize(p, ctxt));
continue;
}
// external type id (or property that depends on it)?
if (ext.handlePropertyValue(p, ctxt, propName, null)) {
continue;
}
// Things marked as ignorable should not be passed to any setter
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
}
// "any property"?
if (_anySetter != null) {
buffer.bufferAnyProperty(_anySetter, propName,
_anySetter.deserialize(p, ctxt));
}
}
tokens.writeEndObject();
// We hit END_OBJECT; resolve the pieces:
try {
return ext.complete(p, ctxt, buffer, creator);
} catch (Exception e) {
return wrapInstantiationProblem(e, ctxt);
}
}
/**
* Helper method for getting a lazily construct exception to be reported
* to {@link DeserializationContext#handleInstantiationProblem(Class, Object, Throwable)}.
*
* @since 2.8
*/
protected Exception _creatorReturnedNullException() {
if (_nullFromCreator == null) {
_nullFromCreator = new NullPointerException("JSON Creator returned null");
}
return _nullFromCreator;
}
/**
* @since 2.8
*/
static class BeanReferring extends Referring
{
private final DeserializationContext _context;
private final SettableBeanProperty _prop;
private Object _bean;
BeanReferring(DeserializationContext ctxt, UnresolvedForwardReference ref,
JavaType valueType, PropertyValueBuffer buffer, SettableBeanProperty prop)
{
super(ref, valueType);
_context = ctxt;
_prop = prop;
}
public void setBean(Object bean) {
_bean = bean;
}
@Override
public void handleResolvedForwardReference(Object id, Object value) throws IOException
{
if (_bean == null) {
_context.reportInputMismatch(_prop,
"Cannot resolve ObjectId forward reference using property '%s' (of type %s): Bean not yet resolved",
_prop.getName(), _prop.getDeclaringClass().getName());
}
_prop.set(_bean, value);
}
}
}