Skip to content

Commit 72ce52f

Browse files
committed
Merge branch 'custom-id-resolution' of https://github.com/pgelinas/jackson-databind into pgelinas-custom-id-resolution
Conflicts: src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
2 parents 7d0445b + 184cae3 commit 72ce52f

16 files changed

+243
-35
lines changed

src/main/java/com/fasterxml/jackson/databind/DatabindContext.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.lang.reflect.Type;
44

55
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
6-
6+
import com.fasterxml.jackson.annotation.ObjectIdResolver;
77
import com.fasterxml.jackson.databind.annotation.NoClass;
88
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
99
import com.fasterxml.jackson.databind.cfg.MapperConfig;
@@ -159,7 +159,20 @@ public ObjectIdGenerator<?> objectIdGeneratorInstance(Annotated annotated,
159159
}
160160
return gen.forScope(objectIdInfo.getScope());
161161
}
162-
162+
163+
public ObjectIdResolver objectIdResolverInstance(Annotated annotated, ObjectIdInfo objectIdInfo)
164+
{
165+
Class<? extends ObjectIdResolver> implClass = objectIdInfo.getResolverType();
166+
final MapperConfig<?> config = getConfig();
167+
HandlerInstantiator hi = config.getHandlerInstantiator();
168+
ObjectIdResolver resolver = (hi == null) ? null : hi.resolverIdGeneratorInstance(config, annotated, implClass);
169+
if (resolver == null) {
170+
resolver = ClassUtil.createInstance(implClass, config.canOverrideAccessModifiers());
171+
}
172+
173+
return resolver;
174+
}
175+
163176
/**
164177
* Helper method to use to construct a {@link Converter}, given a definition
165178
* that may be either actual converter instance, or Class for instantiating one.

src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.concurrent.atomic.AtomicReference;
88

99
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
10+
import com.fasterxml.jackson.annotation.ObjectIdResolver;
1011
import com.fasterxml.jackson.core.*;
1112
import com.fasterxml.jackson.databind.cfg.ContextAttributes;
1213
import com.fasterxml.jackson.databind.deser.*;
@@ -15,6 +16,7 @@
1516
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
1617
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
1718
import com.fasterxml.jackson.databind.introspect.Annotated;
19+
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
1820
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
1921
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
2022
import com.fasterxml.jackson.databind.type.TypeFactory;
@@ -420,8 +422,10 @@ public final KeyDeserializer findKeyDeserializer(JavaType keyType,
420422
* Method called to find and return entry corresponding to given
421423
* Object Id: will add an entry if necessary, and never returns null
422424
*/
423-
public abstract ReadableObjectId findObjectId(Object id,
424-
ObjectIdGenerator<?> generator);
425+
public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> generator, ObjectIdResolver resolver);
426+
427+
@Deprecated
428+
public abstract ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> generator);
425429

426430
/**
427431
* Method called to ensure that every object id encounter during processing

src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.fasterxml.jackson.databind.cfg;
22

33
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
4+
import com.fasterxml.jackson.annotation.ObjectIdResolver;
45
import com.fasterxml.jackson.databind.*;
56
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
67
import com.fasterxml.jackson.databind.introspect.Annotated;
@@ -123,6 +124,11 @@ public ObjectIdGenerator<?> objectIdGeneratorInstance(MapperConfig<?> config,
123124
return null;
124125
}
125126

127+
public ObjectIdResolver resolverIdGeneratorInstance(MapperConfig<?> config, Annotated annotated, Class<?> implClass)
128+
{
129+
return null;
130+
}
131+
126132
/**
127133
* Method called to construct a NamingStrategy instance used for specified
128134
* class.
@@ -143,4 +149,5 @@ public Converter<?,?> converterInstance(MapperConfig<?> config,
143149
Annotated annotated, Class<?> implClass) {
144150
return null;
145151
}
152+
146153
}

src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,9 @@ protected Object _deserializeFromObjectId(JsonParser jp, DeserializationContext
196196
throws IOException, JsonProcessingException
197197
{
198198
Object id = _objectIdReader.readObjectReference(jp, ctxt);
199-
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator);
199+
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
200200
// do we have it resolved?
201-
Object pojo = roid.item;
201+
Object pojo = roid.resolve();
202202
if (pojo == null) { // not yet; should wait...
203203
throw new IllegalStateException("Could not resolve Object Id ["+id+"] -- unresolved forward-reference?");
204204
}

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import com.fasterxml.jackson.annotation.JsonTypeInfo;
1010
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
1111
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
12+
import com.fasterxml.jackson.annotation.ObjectIdResolver;
13+
1214
import com.fasterxml.jackson.core.*;
1315
import com.fasterxml.jackson.databind.*;
1416
import com.fasterxml.jackson.databind.deser.impl.*;
@@ -577,6 +579,7 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
577579
JavaType idType;
578580
SettableBeanProperty idProp;
579581
ObjectIdGenerator<?> idGen;
582+
ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
580583
if (implClass == ObjectIdGenerators.PropertyGenerator.class) {
581584
PropertyName propName = objectIdInfo.getPropertyName();
582585
idProp = findProperty(propName);
@@ -594,7 +597,7 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
594597
}
595598
JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
596599
oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(),
597-
idGen, deser, idProp);
600+
idGen, deser, idProp, resolver);
598601
}
599602
}
600603
// either way, need to resolve serializer:
@@ -965,7 +968,7 @@ protected Object _handleTypedObjectId(JsonParser jp, DeserializationContext ctxt
965968
id = _convertObjectId(jp, ctxt, rawId, idDeser);
966969
}
967970

968-
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator);
971+
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
969972
roid.bindItem(pojo);
970973
// also: may need to set a property value as well
971974
SettableBeanProperty idProp = _objectIdReader.idProperty;
@@ -1028,9 +1031,9 @@ protected Object deserializeFromObjectId(JsonParser jp, DeserializationContext c
10281031
throws IOException, JsonProcessingException
10291032
{
10301033
Object id = _objectIdReader.readObjectReference(jp, ctxt);
1031-
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator);
1034+
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
10321035
// do we have it resolved?
1033-
Object pojo = roid.item;
1036+
Object pojo = roid.resolve();
10341037
if (pojo == null) { // not yet; should wait...
10351038
throw new UnresolvedForwardReference("Could not resolve Object Id ["+id+"] (for "
10361039
+_beanType+").", jp.getCurrentLocation(), roid);

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
88
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
9-
9+
import com.fasterxml.jackson.annotation.ObjectIdResolver;
1010
import com.fasterxml.jackson.databind.*;
1111
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
1212
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
@@ -369,6 +369,8 @@ protected void addObjectIdReader(DeserializationContext ctxt,
369369
SettableBeanProperty idProp;
370370
ObjectIdGenerator<?> gen;
371371

372+
ObjectIdResolver resolver = ctxt.objectIdResolverInstance(beanDesc.getClassInfo(), objectIdInfo);
373+
372374
// Just one special case: Property-based generator is trickier
373375
if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work
374376
PropertyName propName = objectIdInfo.getPropertyName();
@@ -388,7 +390,7 @@ protected void addObjectIdReader(DeserializationContext ctxt,
388390
// also: unlike with value deserializers, let's just resolve one we need here
389391
JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
390392
builder.setObjectIdReader(ObjectIdReader.construct(idType,
391-
objectIdInfo.getPropertyName(), gen, deser, idProp));
393+
objectIdInfo.getPropertyName(), gen, deser, idProp, resolver));
392394
}
393395

394396
@SuppressWarnings("unchecked")

src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java

+42-10
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package com.fasterxml.jackson.databind.deser;
22

3+
import java.util.ArrayList;
34
import java.util.Iterator;
45
import java.util.LinkedHashMap;
6+
import java.util.List;
57
import java.util.Map.Entry;
68

79
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
10+
import com.fasterxml.jackson.annotation.ObjectIdResolver;
811
import com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey;
12+
import com.fasterxml.jackson.annotation.SimpleObjectIdResolver;
913
import com.fasterxml.jackson.core.JsonParser;
1014
import com.fasterxml.jackson.databind.*;
1115
import com.fasterxml.jackson.databind.annotation.NoClass;
@@ -32,6 +36,8 @@ public abstract class DefaultDeserializationContext
3236

3337
protected transient LinkedHashMap<ObjectIdGenerator.IdKey, ReadableObjectId> _objectIds;
3438

39+
private List<ObjectIdResolver> _objectIdResolvers;
40+
3541
/**
3642
* Constructor that will pass specified deserializer factory and
3743
* cache: cache may be null (in which case default implementation
@@ -58,44 +64,70 @@ protected DefaultDeserializationContext(DefaultDeserializationContext src,
5864
*/
5965

6066
@Override
61-
public ReadableObjectId findObjectId(Object id,
62-
ObjectIdGenerator<?> generator)
67+
public ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> generator, ObjectIdResolver resolverType)
6368
{
6469
final ObjectIdGenerator.IdKey key = generator.key(id);
6570
if (_objectIds == null) {
66-
_objectIds = new LinkedHashMap<ObjectIdGenerator.IdKey, ReadableObjectId>();
71+
_objectIds = new LinkedHashMap<ObjectIdGenerator.IdKey,ReadableObjectId>();
6772
} else {
6873
ReadableObjectId entry = _objectIds.get(key);
6974
if (entry != null) {
7075
return entry;
7176
}
7277
}
73-
ReadableObjectId entry = new ReadableObjectId(id);
78+
79+
// Not seen yet, must create entry and configure resolver.
80+
ObjectIdResolver resolver = null;
81+
82+
if (_objectIdResolvers == null) {
83+
_objectIdResolvers = new ArrayList<ObjectIdResolver>(8);
84+
} else {
85+
for (ObjectIdResolver res : _objectIdResolvers) {
86+
if (res.canUseFor(resolverType)) {
87+
resolver = res;
88+
break;
89+
}
90+
}
91+
}
92+
if (resolver == null) {
93+
resolver = resolverType.newForDeserialization(this);
94+
_objectIdResolvers.add(resolver);
95+
}
96+
97+
ReadableObjectId entry = new ReadableObjectId(key);
98+
entry.setResolver(resolver);
7499
_objectIds.put(key, entry);
75100
return entry;
76101
}
77102

78103
@Override
79-
public void checkUnresolvedObjectId() throws UnresolvedForwardReference
104+
public ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> generator)
80105
{
81-
if(_objectIds == null){
106+
return findObjectId(id, generator, new SimpleObjectIdResolver());
107+
}
108+
109+
@Override
110+
public void checkUnresolvedObjectId()
111+
throws UnresolvedForwardReference
112+
{
113+
if (_objectIds == null) {
82114
return;
83115
}
84116

85117
UnresolvedForwardReference exception = null;
86118
for (Entry<IdKey,ReadableObjectId> entry : _objectIds.entrySet()) {
87119
ReadableObjectId roid = entry.getValue();
88-
if(roid.hasReferringProperties()){
89-
if(exception == null){
120+
if (roid.hasReferringProperties()) {
121+
if (exception == null) {
90122
exception = new UnresolvedForwardReference("Unresolved forward references for: ");
91123
}
92124
for (Iterator<Referring> iterator = roid.referringProperties(); iterator.hasNext();) {
93125
Referring referring = iterator.next();
94-
exception.addUnresolvedId(roid.id, referring.getBeanType(), referring.getLocation());
126+
exception.addUnresolvedId(roid.getKey().key, referring.getBeanType(), referring.getLocation());
95127
}
96128
}
97129
}
98-
if(exception != null){
130+
if (exception != null) {
99131
throw exception;
100132
}
101133
}

src/main/java/com/fasterxml/jackson/databind/deser/UnresolvedForwardReference.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public ReadableObjectId getRoid() {
4040
}
4141

4242
public Object getUnresolvedId() {
43-
return _roid.id;
43+
return _roid.getKey().key;
4444
}
4545

4646
public void addUnresolvedId(Object id, Class<?> type, JsonLocation where) {

src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java

+26-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import java.io.IOException;
44

55
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
6+
import com.fasterxml.jackson.annotation.ObjectIdResolver;
7+
import com.fasterxml.jackson.annotation.SimpleObjectIdResolver;
68
import com.fasterxml.jackson.core.JsonParser;
79
import com.fasterxml.jackson.core.JsonProcessingException;
810
import com.fasterxml.jackson.databind.*;
@@ -26,7 +28,12 @@ public class ObjectIdReader
2628
* the key.
2729
*/
2830
public final ObjectIdGenerator<?> generator;
29-
31+
32+
/**
33+
*
34+
*/
35+
public final ObjectIdResolver resolver;
36+
3037
/**
3138
* Deserializer used for deserializing id values.
3239
*/
@@ -42,15 +49,23 @@ public class ObjectIdReader
4249

4350
@SuppressWarnings("unchecked")
4451
protected ObjectIdReader(JavaType t, PropertyName propName, ObjectIdGenerator<?> gen,
45-
JsonDeserializer<?> deser, SettableBeanProperty idProp)
52+
JsonDeserializer<?> deser, SettableBeanProperty idProp, ObjectIdResolver resolver)
4653
{
4754
_idType = t;
4855
propertyName = propName;
4956
generator = gen;
57+
this.resolver = resolver;
5058
_deserializer = (JsonDeserializer<Object>) deser;
5159
idProperty = idProp;
5260
}
5361

62+
@Deprecated // since 2.4
63+
protected ObjectIdReader(JavaType t, PropertyName propName, ObjectIdGenerator<?> gen,
64+
JsonDeserializer<?> deser, SettableBeanProperty idProp)
65+
{
66+
this(t,propName, gen, deser, idProp, new SimpleObjectIdResolver());
67+
}
68+
5469
@Deprecated // since 2.3
5570
protected ObjectIdReader(JavaType t, String propName, ObjectIdGenerator<?> gen,
5671
JsonDeserializer<?> deser, SettableBeanProperty idProp)
@@ -63,11 +78,19 @@ protected ObjectIdReader(JavaType t, String propName, ObjectIdGenerator<?> gen,
6378
* with the initial information based on standard settings for the type
6479
* for which serializer is being built.
6580
*/
81+
public static ObjectIdReader construct(JavaType idType, PropertyName propName,
82+
ObjectIdGenerator<?> generator, JsonDeserializer<?> deser,
83+
SettableBeanProperty idProp, ObjectIdResolver resolver)
84+
{
85+
return new ObjectIdReader(idType, propName, generator, deser, idProp, resolver);
86+
}
87+
88+
@Deprecated // since 2.4
6689
public static ObjectIdReader construct(JavaType idType, PropertyName propName,
6790
ObjectIdGenerator<?> generator, JsonDeserializer<?> deser,
6891
SettableBeanProperty idProp)
6992
{
70-
return new ObjectIdReader(idType, propName, generator, deser, idProp);
93+
return construct(idType, propName, generator, deser, idProp, new SimpleObjectIdResolver());
7194
}
7295

7396
@Deprecated // since 2.3

src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public Object deserializeSetAndReturn(JsonParser jp,
9191
{
9292
// note: no null checks (unlike usually); deserializer should fail if one found
9393
Object id = _valueDeserializer.deserialize(jp, ctxt);
94-
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator);
94+
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
9595
roid.bindItem(instance);
9696
// also: may need to set a property value as well
9797
SettableBeanProperty idProp = _objectIdReader.idProperty;

src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public Object handleIdValue(final DeserializationContext ctxt, Object bean)
113113
{
114114
if (_objectIdReader != null) {
115115
if (_idValue != null) {
116-
ReadableObjectId roid = ctxt.findObjectId(_idValue, _objectIdReader.generator);
116+
ReadableObjectId roid = ctxt.findObjectId(_idValue, _objectIdReader.generator, _objectIdReader.resolver);
117117
roid.bindItem(bean);
118118
// also: may need to set a property value as well
119119
SettableBeanProperty idProp = _objectIdReader.idProperty;

0 commit comments

Comments
 (0)