From 184cae3c98f8af8813afebe875b5a9bc2b2bbe4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20G=C3=A9linas?= Date: Wed, 5 Feb 2014 17:35:56 -0500 Subject: [PATCH] Implementation of custom object id resolution. --- .../jackson/databind/DatabindContext.java | 17 ++++- .../databind/DeserializationContext.java | 8 +- .../databind/cfg/HandlerInstantiator.java | 7 ++ .../databind/deser/AbstractDeserializer.java | 4 +- .../databind/deser/BeanDeserializerBase.java | 12 +-- .../deser/BeanDeserializerFactory.java | 6 +- .../deser/DefaultDeserializationContext.java | 52 ++++++++++--- .../deser/UnresolvedForwardReference.java | 2 +- .../databind/deser/impl/ObjectIdReader.java | 29 +++++++- .../deser/impl/ObjectIdValueProperty.java | 2 +- .../deser/impl/PropertyValueBuffer.java | 2 +- .../databind/deser/impl/ReadableObjectId.java | 39 +++++++++- .../JacksonAnnotationIntrospector.java | 2 +- .../databind/introspect/ObjectIdInfo.java | 22 +++++- .../databind/ser/std/BeanSerializerBase.java | 2 +- .../struct/TestObjectIdDeserialization.java | 73 +++++++++++++++++++ 16 files changed, 242 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java index 816a674921..9a20f5c214 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java +++ b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java @@ -3,7 +3,7 @@ import java.lang.reflect.Type; import com.fasterxml.jackson.annotation.ObjectIdGenerator; - +import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.databind.annotation.NoClass; import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; import com.fasterxml.jackson.databind.cfg.MapperConfig; @@ -159,7 +159,20 @@ public ObjectIdGenerator objectIdGeneratorInstance(Annotated annotated, } return gen.forScope(objectIdInfo.getScope()); } - + + public ObjectIdResolver objectIdResolverInstance(Annotated annotated, ObjectIdInfo objectIdInfo) + { + Class implClass = objectIdInfo.getResolverType(); + final MapperConfig config = getConfig(); + HandlerInstantiator hi = config.getHandlerInstantiator(); + ObjectIdResolver resolver = (hi == null) ? null : hi.resolverIdGeneratorInstance(config, annotated, implClass); + if (resolver == null) { + resolver = ClassUtil.createInstance(implClass, config.canOverrideAccessModifiers()); + } + + return resolver; + } + /** * Helper method to use to construct a {@link Converter}, given a definition * that may be either actual converter instance, or Class for instantiating one. diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java index deec697ff9..f2fdbf152a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java +++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java @@ -7,6 +7,7 @@ import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.cfg.ContextAttributes; import com.fasterxml.jackson.databind.deser.*; @@ -15,6 +16,7 @@ import com.fasterxml.jackson.databind.exc.InvalidFormatException; import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -420,8 +422,10 @@ public final KeyDeserializer findKeyDeserializer(JavaType keyType, * Method called to find and return entry corresponding to given * Object Id: will add an entry if necessary, and never returns null */ - public abstract ReadableObjectId findObjectId(Object id, - ObjectIdGenerator generator); + public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator generator, ObjectIdResolver resolver); + + @Deprecated + public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator generator); /** * Method called to ensure that every object id encounter during processing diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java index 4cc0eb6ffe..2ac65e22fd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java +++ b/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java @@ -1,6 +1,7 @@ package com.fasterxml.jackson.databind.cfg; import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.ValueInstantiator; import com.fasterxml.jackson.databind.introspect.Annotated; @@ -123,6 +124,11 @@ public ObjectIdGenerator objectIdGeneratorInstance(MapperConfig config, return null; } + public ObjectIdResolver resolverIdGeneratorInstance(MapperConfig config, Annotated annotated, Class implClass) + { + return null; + } + /** * Method called to construct a NamingStrategy instance used for specified * class. @@ -143,4 +149,5 @@ public Converter converterInstance(MapperConfig config, Annotated annotated, Class implClass) { return null; } + } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java index 96848ed776..b31ccb1fad 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java @@ -196,9 +196,9 @@ protected Object _deserializeFromObjectId(JsonParser jp, DeserializationContext throws IOException, JsonProcessingException { Object id = _objectIdReader.readObjectReference(jp, ctxt); - ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator); + ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); // do we have it resolved? - Object pojo = roid.item; + Object pojo = roid.resolve(); if (pojo == null) { // not yet; should wait... throw new IllegalStateException("Could not resolve Object Id ["+id+"] -- unresolved forward-reference?"); } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java index 171de0568e..fd68fc1aea 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java @@ -9,9 +9,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.annotation.ObjectIdGenerators; - +import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.core.*; - import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.*; import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer; @@ -579,6 +578,7 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, JavaType idType; SettableBeanProperty idProp; ObjectIdGenerator idGen; + ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo); if (implClass == ObjectIdGenerators.PropertyGenerator.class) { PropertyName propName = objectIdInfo.getPropertyName(); idProp = findProperty(propName); @@ -596,7 +596,7 @@ public JsonDeserializer createContextual(DeserializationContext ctxt, } JsonDeserializer deser = ctxt.findRootValueDeserializer(idType); oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(), - idGen, deser, idProp); + idGen, deser, idProp, resolver); } } // either way, need to resolve serializer: @@ -967,7 +967,7 @@ protected Object _handleTypedObjectId(JsonParser jp, DeserializationContext ctxt id = _convertObjectId(jp, ctxt, rawId, idDeser); } - ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator); + ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); roid.bindItem(pojo); // also: may need to set a property value as well SettableBeanProperty idProp = _objectIdReader.idProperty; @@ -1070,9 +1070,9 @@ protected Object deserializeFromObjectId(JsonParser jp, DeserializationContext c throws IOException, JsonProcessingException { Object id = _objectIdReader.readObjectReference(jp, ctxt); - ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator); + ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); // do we have it resolved? - Object pojo = roid.item; + Object pojo = roid.resolve(); if (pojo == null) { // not yet; should wait... throw new UnresolvedForwardReference("Could not resolve Object Id ["+id+"] (for " +_beanType+").", jp.getCurrentLocation(), roid); diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java index 720f33d9d0..1a524f8051 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.ObjectIdGenerator; import com.fasterxml.jackson.annotation.ObjectIdGenerators; - +import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig; @@ -369,6 +369,8 @@ protected void addObjectIdReader(DeserializationContext ctxt, SettableBeanProperty idProp; ObjectIdGenerator gen; + ObjectIdResolver resolver = ctxt.objectIdResolverInstance(beanDesc.getClassInfo(), objectIdInfo); + // Just one special case: Property-based generator is trickier if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work PropertyName propName = objectIdInfo.getPropertyName(); @@ -388,7 +390,7 @@ protected void addObjectIdReader(DeserializationContext ctxt, // also: unlike with value deserializers, let's just resolve one we need here JsonDeserializer deser = ctxt.findRootValueDeserializer(idType); builder.setObjectIdReader(ObjectIdReader.construct(idType, - objectIdInfo.getPropertyName(), gen, deser, idProp)); + objectIdInfo.getPropertyName(), gen, deser, idProp, resolver)); } @SuppressWarnings("unchecked") diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java index be64fb3d1a..e60d4ac381 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java @@ -1,11 +1,15 @@ package com.fasterxml.jackson.databind.deser; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map.Entry; import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey; +import com.fasterxml.jackson.annotation.SimpleObjectIdResolver; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.NoClass; @@ -32,6 +36,8 @@ public abstract class DefaultDeserializationContext protected transient LinkedHashMap _objectIds; + private List _objectIdResolvers; + /** * Constructor that will pass specified deserializer factory and * cache: cache may be null (in which case default implementation @@ -58,44 +64,70 @@ protected DefaultDeserializationContext(DefaultDeserializationContext src, */ @Override - public ReadableObjectId findObjectId(Object id, - ObjectIdGenerator generator) + public ReadableObjectId findObjectId(Object id, ObjectIdGenerator generator, ObjectIdResolver resolverType) { final ObjectIdGenerator.IdKey key = generator.key(id); if (_objectIds == null) { - _objectIds = new LinkedHashMap(); + _objectIds = new LinkedHashMap(); } else { ReadableObjectId entry = _objectIds.get(key); if (entry != null) { return entry; } } - ReadableObjectId entry = new ReadableObjectId(id); + + // Not seen yet, must create entry and configure resolver. + ObjectIdResolver resolver = null; + + if (_objectIdResolvers == null) { + _objectIdResolvers = new ArrayList(8); + } else { + for (ObjectIdResolver res : _objectIdResolvers) { + if (res.canUseFor(resolverType)) { + resolver = res; + break; + } + } + } + if (resolver == null) { + resolver = resolverType.newForDeserialization(this); + _objectIdResolvers.add(resolver); + } + + ReadableObjectId entry = new ReadableObjectId(key); + entry.setResolver(resolver); _objectIds.put(key, entry); return entry; } @Override - public void checkUnresolvedObjectId() throws UnresolvedForwardReference + public ReadableObjectId findObjectId(Object id, ObjectIdGenerator generator) { - if(_objectIds == null){ + return findObjectId(id, generator, new SimpleObjectIdResolver()); + } + + @Override + public void checkUnresolvedObjectId() + throws UnresolvedForwardReference + { + if (_objectIds == null) { return; } UnresolvedForwardReference exception = null; for (Entry entry : _objectIds.entrySet()) { ReadableObjectId roid = entry.getValue(); - if(roid.hasReferringProperties()){ - if(exception == null){ + if (roid.hasReferringProperties()) { + if (exception == null) { exception = new UnresolvedForwardReference("Unresolved forward references for: "); } for (Iterator iterator = roid.referringProperties(); iterator.hasNext();) { Referring referring = iterator.next(); - exception.addUnresolvedId(roid.id, referring.getBeanType(), referring.getLocation()); + exception.addUnresolvedId(roid.getKey().key, referring.getBeanType(), referring.getLocation()); } } } - if(exception != null){ + if (exception != null) { throw exception; } } diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java b/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java index 2f62f5dd1b..15099915fd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java @@ -40,7 +40,7 @@ public ReadableObjectId getRoid() { } public Object getUnresolvedId() { - return _roid.id; + return _roid.getKey().key; } public void addUnresolvedId(Object id, Class type, JsonLocation where) { diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java index 27b75febdf..16dcc4c497 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java @@ -3,6 +3,8 @@ import java.io.IOException; import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.annotation.SimpleObjectIdResolver; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.*; @@ -26,7 +28,12 @@ public class ObjectIdReader * the key. */ public final ObjectIdGenerator generator; - + + /** + * + */ + public final ObjectIdResolver resolver; + /** * Deserializer used for deserializing id values. */ @@ -42,15 +49,23 @@ public class ObjectIdReader @SuppressWarnings("unchecked") protected ObjectIdReader(JavaType t, PropertyName propName, ObjectIdGenerator gen, - JsonDeserializer deser, SettableBeanProperty idProp) + JsonDeserializer deser, SettableBeanProperty idProp, ObjectIdResolver resolver) { _idType = t; propertyName = propName; generator = gen; + this.resolver = resolver; _deserializer = (JsonDeserializer) deser; idProperty = idProp; } + @Deprecated // since 2.4 + protected ObjectIdReader(JavaType t, PropertyName propName, ObjectIdGenerator gen, + JsonDeserializer deser, SettableBeanProperty idProp) + { + this(t,propName, gen, deser, idProp, new SimpleObjectIdResolver()); + } + @Deprecated // since 2.3 protected ObjectIdReader(JavaType t, String propName, ObjectIdGenerator gen, JsonDeserializer deser, SettableBeanProperty idProp) @@ -63,11 +78,19 @@ protected ObjectIdReader(JavaType t, String propName, ObjectIdGenerator gen, * with the initial information based on standard settings for the type * for which serializer is being built. */ + public static ObjectIdReader construct(JavaType idType, PropertyName propName, + ObjectIdGenerator generator, JsonDeserializer deser, + SettableBeanProperty idProp, ObjectIdResolver resolver) + { + return new ObjectIdReader(idType, propName, generator, deser, idProp, resolver); + } + + @Deprecated // since 2.4 public static ObjectIdReader construct(JavaType idType, PropertyName propName, ObjectIdGenerator generator, JsonDeserializer deser, SettableBeanProperty idProp) { - return new ObjectIdReader(idType, propName, generator, deser, idProp); + return construct(idType, propName, generator, deser, idProp, new SimpleObjectIdResolver()); } @Deprecated // since 2.3 diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java index d5e67628ca..60610f0c6f 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java @@ -91,7 +91,7 @@ public Object deserializeSetAndReturn(JsonParser jp, { // note: no null checks (unlike usually); deserializer should fail if one found Object id = _valueDeserializer.deserialize(jp, ctxt); - ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator); + ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver); roid.bindItem(instance); // also: may need to set a property value as well SettableBeanProperty idProp = _objectIdReader.idProperty; diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java index c96bb7ef4b..d899d44503 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java @@ -113,7 +113,7 @@ public Object handleIdValue(final DeserializationContext ctxt, Object bean) { if (_objectIdReader != null) { if (_idValue != null) { - ReadableObjectId roid = ctxt.findObjectId(_idValue, _objectIdReader.generator); + ReadableObjectId roid = ctxt.findObjectId(_idValue, _objectIdReader.generator, _objectIdReader.resolver); roid.bindItem(bean); // also: may need to set a property value as well SettableBeanProperty idProp = _objectIdReader.idProperty; diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java index e6362a3002..82c501d737 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java @@ -5,6 +5,8 @@ import java.util.Iterator; import java.util.LinkedList; +import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey; +import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.core.JsonLocation; /** @@ -13,15 +15,42 @@ */ public class ReadableObjectId { + /** + * @deprecated Prefer using {@link #resolve()}, which is able to handle + * external id resolving mechanism. + */ + @Deprecated + public Object item; + @Deprecated public final Object id; - public Object item; + private final IdKey _key; private LinkedList _referringProperties; + private ObjectIdResolver _resolver; + + @Deprecated public ReadableObjectId(Object id) { this.id = id; + _key = null; + } + + public ReadableObjectId(IdKey key) + { + _key = key; + id = key.key; + } + + public void setResolver(ObjectIdResolver resolver) + { + _resolver = resolver; + } + + public IdKey getKey() + { + return _key; } public void appendReferring(Referring currentReferring) { @@ -37,9 +66,7 @@ public void appendReferring(Referring currentReferring) { */ public void bindItem(Object ob) throws IOException { - if (item != null) { - throw new IllegalStateException("Already had POJO for id (" + id.getClass().getName() + ") [" + id + "]"); - } + _resolver.bindItem(_key, ob); item = ob; if (_referringProperties != null) { Iterator it = _referringProperties.iterator(); @@ -50,6 +77,10 @@ public void bindItem(Object ob) throws IOException } } + public Object resolve(){ + return (item = _resolver.resolveId(_key)); + } + public boolean hasReferringProperties() { return (_referringProperties != null) && !_referringProperties.isEmpty(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java index 8647e69bd4..09102c32d1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java @@ -459,7 +459,7 @@ public ObjectIdInfo findObjectIdInfo(Annotated ann) { } // In future may need to allow passing namespace? PropertyName name = new PropertyName(info.property()); - return new ObjectIdInfo(name, info.scope(), info.generator()); + return new ObjectIdInfo(name, info.scope(), info.generator(), info.resolver()); } @Override diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java index 8ec44edd2e..13f5c82af7 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.ObjectIdGenerator; +import com.fasterxml.jackson.annotation.ObjectIdResolver; +import com.fasterxml.jackson.annotation.SimpleObjectIdResolver; import com.fasterxml.jackson.databind.PropertyName; /** @@ -13,10 +15,19 @@ public class ObjectIdInfo { protected final PropertyName _propertyName; protected final Class> _generator; + private final Class _resolver; protected final Class _scope; protected final boolean _alwaysAsId; - public ObjectIdInfo(PropertyName name, Class scope, Class> gen) { + public ObjectIdInfo(PropertyName name, Class scope, Class> gen, + Class resolver) + { + this(name, scope, gen, false, resolver); + } + + @Deprecated // since 2.4 + public ObjectIdInfo(PropertyName name, Class scope, Class> gen) + { this(name, scope, gen, false); } @@ -27,11 +38,19 @@ public ObjectIdInfo(String name, Class scope, Class scope, Class> gen, boolean alwaysAsId) + { + this(prop, scope, gen, alwaysAsId, SimpleObjectIdResolver.class); + + } + + protected ObjectIdInfo(PropertyName prop, Class scope, Class> gen, + boolean alwaysAsId, Class resolver) { _propertyName = prop; _scope = scope; _generator = gen; _alwaysAsId = alwaysAsId; + _resolver = resolver; } public ObjectIdInfo withAlwaysAsId(boolean state) { @@ -44,6 +63,7 @@ public ObjectIdInfo withAlwaysAsId(boolean state) { public PropertyName getPropertyName() { return _propertyName; } public Class getScope() { return _scope; } public Class> getGeneratorType() { return _generator; } + public Class getResolverType() { return _resolver; } public boolean getAlwaysAsId() { return _alwaysAsId; } @Override diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java index efd4adef4c..c136a2fca1 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java @@ -389,7 +389,7 @@ public JsonSerializer createContextual(SerializerProvider provider, // no ObjectId override, but maybe ObjectIdRef? if (oiw != null) { objectIdInfo = intr.findObjectReferenceInfo(accessor, - new ObjectIdInfo(NAME_FOR_OBJECT_REF, null, null)); + new ObjectIdInfo(NAME_FOR_OBJECT_REF, null, null, null)); oiw = _objectIdWriter.withAlwaysAsId(objectIdInfo.getAlwaysAsId()); } } else { diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java index f9b0758ac7..ce6c047ec4 100644 --- a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java +++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java @@ -8,9 +8,13 @@ import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey; import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.fasterxml.jackson.annotation.ObjectIdResolver; import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.cfg.ContextAttributes; import com.fasterxml.jackson.databind.deser.UnresolvedForwardReference; import com.fasterxml.jackson.databind.deser.UnresolvedId; import com.fasterxml.jackson.databind.struct.TestObjectId.Company; @@ -21,6 +25,8 @@ */ public class TestObjectIdDeserialization extends BaseMapTest { + private static final String POOL_KEY = "POOL"; + // // Classes for external id use @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id") @@ -129,6 +135,50 @@ public void anySet(String field, AnySetterObjectId value) { } } + static class CustomResolutionWrapper { + public List data; + } + + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", resolver = PoolResolver.class) + static class WithCustomResolution { + public int id; + public int data; + + public WithCustomResolution(int id, int data) + { + this.id = id; + this.data = data; + } + } + + public static class PoolResolver implements ObjectIdResolver { + private Map _pool; + + public PoolResolver() {} + public PoolResolver(Map pool){ _pool = pool; } + + @Override + public void bindItem(IdKey id, Object pojo){ } + + @Override + public Object resolveId(IdKey id){ return _pool.get(id.key); } + + @Override + public boolean canUseFor(ObjectIdResolver resolverType) + { + return resolverType.getClass() == getClass() && _pool != null && !_pool.isEmpty(); + } + + @Override + public ObjectIdResolver newForDeserialization(Object c) + { + DeserializationContext context = (DeserializationContext)c; + @SuppressWarnings("unchecked") + Map pool = (Map)context.getAttribute(POOL_KEY); + return new PoolResolver(pool); + } + } + private final ObjectMapper mapper = new ObjectMapper(); /* @@ -343,4 +393,27 @@ public void testCustomDeserializationProperty() throws Exception assertSame(result.node, result.node.next.node); assertEquals(3, result.node.customId); } + + /* + /***************************************************** + /* Unit tests, custom id resolver + /***************************************************** + */ + public void testCustomPoolResolver() + throws Exception + { + Map pool = new HashMap(); + pool.put(1, new WithCustomResolution(1, 1)); + pool.put(2, new WithCustomResolution(2, 2)); + pool.put(3, new WithCustomResolution(3, 3)); + pool.put(4, new WithCustomResolution(4, 4)); + pool.put(5, new WithCustomResolution(5, 5)); + ContextAttributes attrs = mapper.getDeserializationConfig().getAttributes().withSharedAttribute(POOL_KEY, pool); + String content = "{\"data\":[1,2,3,4,5]}"; + CustomResolutionWrapper wrapper = mapper.reader(CustomResolutionWrapper.class).with(attrs).readValue(content); + assertFalse(wrapper.data.isEmpty()); + for (WithCustomResolution ob : wrapper.data) { + assertSame(pool.get(ob.id), ob); + } + } }