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

com.groupbyinc.common.jackson.databind.deser.BuilderBasedDeserializer Maven / Gradle / Ivy

There is a newer version: 198
Show newest version
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.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.databind.util.TokenBuffer;

/**
 * Class that handles deserialization using a separate
 * Builder class, which is used for data binding and
 * produces actual deserialized value at the end
 * of data binding.
 *

* Note on implementation: much of code has been copied from * {@link BeanDeserializer}; there may be opportunities to * refactor this in future. */ public class BuilderBasedDeserializer extends BeanDeserializerBase { private static final long serialVersionUID = 1L; protected final AnnotatedMethod _buildMethod; /** * Type that the builder will produce, target type; as opposed to * `handledType()` which refers to Builder class. * * @since 2.9 */ protected final JavaType _targetType; /* /********************************************************** /* Life-cycle, construction, initialization /********************************************************** */ /** * Constructor used by {@link BeanDeserializerBuilder}. */ public BuilderBasedDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, JavaType targetType, BeanPropertyMap properties, Map backRefs, Set ignorableProps, boolean ignoreAllUnknown, boolean hasViews) { super(builder, beanDesc, properties, backRefs, ignorableProps, ignoreAllUnknown, hasViews); _targetType = targetType; _buildMethod = builder.getBuildMethod(); // 05-Mar-2012, tatu: Cannot really make Object Ids work with builders, not yet anyway if (_objectIdReader != null) { throw new IllegalArgumentException("Cannot use Object Id with Builder-based deserialization (type " +beanDesc.getType()+")"); } } /** * @deprecated Since 2.9 */ @Deprecated public BuilderBasedDeserializer(BeanDeserializerBuilder builder, BeanDescription beanDesc, BeanPropertyMap properties, Map backRefs, Set ignorableProps, boolean ignoreAllUnknown, boolean hasViews) { this(builder, beanDesc, beanDesc.getType(), // Wrong! But got no access via `BeanDeserializerBuilder` properties, backRefs, ignorableProps, ignoreAllUnknown, hasViews); } /** * Copy-constructor that can be used by sub-classes to allow * copy-on-write styling copying of settings of an existing instance. */ protected BuilderBasedDeserializer(BuilderBasedDeserializer src) { this(src, src._ignoreAllUnknown); } protected BuilderBasedDeserializer(BuilderBasedDeserializer src, boolean ignoreAllUnknown) { super(src, ignoreAllUnknown); _buildMethod = src._buildMethod; _targetType = src._targetType; } protected BuilderBasedDeserializer(BuilderBasedDeserializer src, NameTransformer unwrapper) { super(src, unwrapper); _buildMethod = src._buildMethod; _targetType = src._targetType; } public BuilderBasedDeserializer(BuilderBasedDeserializer src, ObjectIdReader oir) { super(src, oir); _buildMethod = src._buildMethod; _targetType = src._targetType; } public BuilderBasedDeserializer(BuilderBasedDeserializer src, Set ignorableProps) { super(src, ignorableProps); _buildMethod = src._buildMethod; _targetType = src._targetType; } public BuilderBasedDeserializer(BuilderBasedDeserializer src, BeanPropertyMap props) { super(src, props); _buildMethod = src._buildMethod; _targetType = src._targetType; } @Override public JsonDeserializer unwrappingDeserializer(NameTransformer unwrapper) { /* main thing really is to just enforce ignoring of unknown * properties; since there may be multiple unwrapped values * and properties for all may be interleaved... */ return new BuilderBasedDeserializer(this, unwrapper); } @Override public BeanDeserializerBase withObjectIdReader(ObjectIdReader oir) { return new BuilderBasedDeserializer(this, oir); } @Override public BeanDeserializerBase withIgnorableProperties(Set ignorableProps) { return new BuilderBasedDeserializer(this, ignorableProps); } @Override public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) { return new BuilderBasedDeserializer(this, props); } @Override protected BeanDeserializerBase asArrayDeserializer() { SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder(); return new BeanAsArrayBuilderDeserializer(this, _targetType, props, _buildMethod); } /* /********************************************************** /* JsonDeserializer implementation /********************************************************** */ @Override // since 2.9 public Boolean supportsUpdate(DeserializationConfig config) { // 26-Oct-2016, tatu: No, we can't merge Builder-based POJOs as of now return Boolean.FALSE; } /* /********************************************************** /* JsonDeserializer implementation /********************************************************** */ protected Object finishBuild(DeserializationContext ctxt, Object builder) throws IOException { // As per [databind#777], allow returning builder itself if (null == _buildMethod) { return builder; } try { return _buildMethod.getMember().invoke(builder, (Object[]) null); } catch (Exception e) { return wrapInstantiationProblem(e, ctxt); } } /** * Main deserialization method for bean-based objects (POJOs). */ @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // common case first: if (p.isExpectedStartObjectToken()) { JsonToken t = p.nextToken(); if (_vanillaProcessing) { return finishBuild(ctxt, vanillaDeserialize(p, ctxt, t)); } Object builder = deserializeFromObject(p, ctxt); return finishBuild(ctxt, builder); } // and then others, generally requiring use of @JsonCreator switch (p.getCurrentTokenId()) { case JsonTokenId.ID_STRING: return finishBuild(ctxt, deserializeFromString(p, ctxt)); case JsonTokenId.ID_NUMBER_INT: return finishBuild(ctxt, deserializeFromNumber(p, ctxt)); case JsonTokenId.ID_NUMBER_FLOAT: return finishBuild(ctxt, deserializeFromDouble(p, ctxt)); case JsonTokenId.ID_EMBEDDED_OBJECT: return p.getEmbeddedObject(); case JsonTokenId.ID_TRUE: case JsonTokenId.ID_FALSE: return finishBuild(ctxt, deserializeFromBoolean(p, ctxt)); case JsonTokenId.ID_START_ARRAY: // these only work if there's a (delegating) creator... return finishBuild(ctxt, deserializeFromArray(p, ctxt)); case JsonTokenId.ID_FIELD_NAME: case JsonTokenId.ID_END_OBJECT: return finishBuild(ctxt, deserializeFromObject(p, ctxt)); default: } return ctxt.handleUnexpectedToken(handledType(), p); } /** * 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 value) throws IOException { // 26-Oct-2016, tatu: I cannot see any of making this actually // work correctly, so let's indicate problem right away JavaType valueType = _targetType; // Did they try to give us builder? Class builderRawType = handledType(); Class instRawType = value.getClass(); if (builderRawType.isAssignableFrom(instRawType)) { return ctxt.reportBadDefinition(valueType, String.format( "Deserialization of %s by passing existing Builder (%s) instance not supported", valueType, builderRawType.getName())); } return ctxt.reportBadDefinition(valueType, String.format( "Deserialization of %s by passing existing instance (of %s) not supported", valueType, instRawType.getName())); } /* /********************************************************** /* 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 { Object bean = _valueInstantiator.createUsingDefault(ctxt); for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) { String propName = p.getCurrentName(); // Skip field name: p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case try { bean = prop.deserializeSetAndReturn(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } } else { handleUnknownVanilla(p, ctxt, bean, propName); } } return bean; } /** * General version used when handling needs more advanced * features. */ @Override public Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException { if (_nonStandardCreation) { if (_unwrappedPropertyHandler != null) { return deserializeWithUnwrapped(p, ctxt); } if (_externalTypeIdHandler != null) { return deserializeWithExternalTypeId(p, ctxt); } return deserializeFromObjectUsingNonDefault(p, ctxt); } Object bean = _valueInstantiator.createUsingDefault(ctxt); if (_injectables != null) { injectValues(ctxt, bean); } if (_needViewProcesing) { Class view = ctxt.getActiveView(); if (view != null) { return deserializeWithView(p, ctxt, bean, view); } } for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) { String propName = p.getCurrentName(); // Skip field name: p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case try { bean = prop.deserializeSetAndReturn(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } handleUnknownVanilla(p, ctxt, bean, propName); } 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. * * @return Builder instance constructed */ @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); final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases TokenBuffer unknown = null; 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) { if ((activeView != null) && !creatorProp.visibleInView(activeView)) { p.skipChildren(); continue; } // Last creator property to set? if (buffer.assignParameter(creatorProp, creatorProp.deserialize(p, ctxt))) { p.nextToken(); // to move to following FIELD_NAME/END_OBJECT Object builder; try { builder = creator.build(ctxt, buffer); } catch (Exception e) { wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); continue; // never gets here } // polymorphic? if (builder.getClass() != _beanType.getRawClass()) { return handlePolymorphic(p, ctxt, builder, unknown); } if (unknown != null) { // nope, just extra unknown stuff... builder = handleUnknownProperties(ctxt, builder, unknown); } // or just clean? return _deserialize(p, ctxt, builder); } 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; } // As per [JACKSON-313], 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)); 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 builder; try { builder = creator.build(ctxt, buffer); } catch (Exception e) { builder = wrapInstantiationProblem(e, ctxt); } if (unknown != null) { // polymorphic? if (builder.getClass() != _beanType.getRawClass()) { return handlePolymorphic(null, ctxt, builder, unknown); } // no, just some extra unknown properties return handleUnknownProperties(ctxt, builder, unknown); } return builder; } @SuppressWarnings("resource") protected final Object _deserialize(JsonParser p, DeserializationContext ctxt, Object builder) throws IOException { if (_injectables != null) { injectValues(ctxt, builder); } if (_unwrappedPropertyHandler != null) { if (p.hasToken(JsonToken.START_OBJECT)) { p.nextToken(); } TokenBuffer tokens = new TokenBuffer(p, ctxt); tokens.writeStartObject(); return deserializeWithUnwrapped(p, ctxt, builder, tokens); } if (_externalTypeIdHandler != null) { return deserializeWithExternalTypeId(p, ctxt, builder); } if (_needViewProcesing) { Class view = ctxt.getActiveView(); if (view != null) { return deserializeWithView(p, ctxt, builder, view); } } JsonToken t = p.getCurrentToken(); // 23-Mar-2010, tatu: In some cases, we start with full JSON object too... if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { String propName = p.getCurrentName(); // Skip field name: p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case try { builder = prop.deserializeSetAndReturn(p, ctxt, builder); } catch (Exception e) { wrapAndThrow(e, builder, propName, ctxt); } continue; } handleUnknownVanilla(p, ctxt, handledType(), propName); } return builder; } /* /********************************************************** /* Deserializing when we have to consider an active View /********************************************************** */ protected final Object deserializeWithView(JsonParser p, DeserializationContext ctxt, Object bean, Class activeView) throws IOException { JsonToken t = p.getCurrentToken(); for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) { String propName = p.getCurrentName(); // Skip field name: p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { if (!prop.visibleInView(activeView)) { p.skipChildren(); continue; } try { bean = prop.deserializeSetAndReturn(p, ctxt, bean); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } handleUnknownVanilla(p, ctxt, bean, propName); } 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(); Object bean = _valueInstantiator.createUsingDefault(ctxt); if (_injectables != null) { injectValues(ctxt, bean); } final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) { String propName = p.getCurrentName(); p.nextToken(); SettableBeanProperty prop = _beanProperties.find(propName); if (prop != null) { // normal case if (activeView != null && !prop.visibleInView(activeView)) { p.skipChildren(); continue; } try { bean = prop.deserializeSetAndReturn(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 should be passed to unwrapped property deserializers tokens.writeFieldName(propName); tokens.copyCurrentStructure(p); // how about any setter? We'll get copies but... if (_anySetter != null) { try { _anySetter.deserializeAndSet(p, ctxt, bean, propName); } catch (Exception e) { wrapAndThrow(e, bean, propName, ctxt); } continue; } } tokens.writeEndObject(); return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, bean, tokens); } @SuppressWarnings("resource") protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p, DeserializationContext ctxt) throws IOException { final PropertyBasedCreator creator = _propertyBasedCreator; PropertyValueBuffer buffer = creator.startBuilding(p, ctxt, _objectIdReader); TokenBuffer tokens = new TokenBuffer(p, ctxt); tokens.writeStartObject(); Object builder = null; 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, creatorProp.deserialize(p, ctxt))) { t = p.nextToken(); // to move to following FIELD_NAME/END_OBJECT try { builder = creator.build(ctxt, buffer); } catch (Exception e) { wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt); continue; // never gets here } if (builder.getClass() != _beanType.getRawClass()) { return handlePolymorphic(p, ctxt, builder, tokens); } return deserializeWithUnwrapped(p, ctxt, builder, 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, prop.deserialize(p, ctxt)); continue; } if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, handledType(), propName); continue; } tokens.writeFieldName(propName); tokens.copyCurrentStructure(p); // "any property"? if (_anySetter != null) { buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(p, ctxt)); } } tokens.writeEndObject(); // We hit END_OBJECT, so: if (builder == null) { try { builder = creator.build(ctxt, buffer); } catch (Exception e) { return wrapInstantiationProblem(e, ctxt); } } return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, builder, tokens); } protected Object deserializeWithUnwrapped(JsonParser p, DeserializationContext ctxt, Object builder, TokenBuffer tokens) throws IOException { final Class activeView = _needViewProcesing ? ctxt.getActiveView() : null; for (JsonToken t = p.getCurrentToken(); 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 { builder = prop.deserializeSetAndReturn(p, ctxt, builder); } catch (Exception e) { wrapAndThrow(e, builder, propName, ctxt); } continue; } if (_ignorableProps != null && _ignorableProps.contains(propName)) { handleIgnoredProperty(p, ctxt, builder, propName); continue; } // but... others should be passed to unwrapped property deserializers tokens.writeFieldName(propName); tokens.copyCurrentStructure(p); // how about any setter? We'll get copies but... if (_anySetter != null) { _anySetter.deserializeAndSet(p, ctxt, builder, propName); } } tokens.writeEndObject(); return _unwrappedPropertyHandler.processUnwrapped(p, ctxt, builder, 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); } 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 // 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 { bean = prop.deserializeSetAndReturn(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; } else { // 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); } protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser p, DeserializationContext ctxt) throws IOException { // !!! 04-Mar-2012, TODO: Need to fix -- will not work as is... JavaType t = _targetType; return ctxt.reportBadDefinition(t, String.format( "Deserialization (of %s) with Builder, External type id, @JsonCreator not yet implemented", t)); } }