Skip to content

Commit efb024e

Browse files
committed
Fix #1551
1 parent 3ee81a6 commit efb024e

File tree

6 files changed

+104
-28
lines changed

6 files changed

+104
-28
lines changed

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ Project: jackson-databind
7474
(reported by Lyor G)
7575
#1550: Unexpected behavior with `@JsonInclude(JsonInclude.Include.NON_EMPTY)` and
7676
`java.util.Date` serialization
77+
#1551: `JsonMappingException` with polymorphic type and `JsonIdentityInfo` when basic type is abstract
78+
(reported by acm073@github)
7779
#1552: Map key converted to byte array is not serialized as base64 string
7880
(reported by nmatt@github)
7981
#1554: Support deserialization of `Shape.OBJECT` ("as POJO") for `Map`s (and map-like types)

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

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import com.fasterxml.jackson.databind.*;
1313
import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
14+
import com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator;
1415
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
1516
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
1617
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
@@ -35,6 +36,8 @@ public class AbstractDeserializer
3536

3637
protected final Map<String, SettableBeanProperty> _backRefProperties;
3738

39+
protected transient Map<String,SettableBeanProperty> _properties;
40+
3841
// support for "native" types, which require special care:
3942

4043
protected final boolean _acceptString;
@@ -48,19 +51,34 @@ public class AbstractDeserializer
4851
/**********************************************************
4952
*/
5053

54+
/**
55+
* @since 2.9
56+
*
57+
* @param props Regular properties: currently only needed to support property-annotated
58+
* Object Id handling with property inclusion (needed for determining type of Object Id
59+
* to bind)
60+
*/
5161
public AbstractDeserializer(BeanDeserializerBuilder builder,
52-
BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps)
62+
BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps,
63+
Map<String, SettableBeanProperty> props)
5364
{
5465
_baseType = beanDesc.getType();
5566
_objectIdReader = builder.getObjectIdReader();
5667
_backRefProperties = backRefProps;
68+
_properties = props;
5769
Class<?> cls = _baseType.getRawClass();
5870
_acceptString = cls.isAssignableFrom(String.class);
5971
_acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class);
6072
_acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class);
6173
_acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class);
6274
}
6375

76+
@Deprecated // since 2.9
77+
public AbstractDeserializer(BeanDeserializerBuilder builder,
78+
BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps) {
79+
this(builder, beanDesc, backRefProps, null);
80+
}
81+
6482
protected AbstractDeserializer(BeanDescription beanDesc)
6583
{
6684
_baseType = beanDesc.getType();
@@ -77,7 +95,7 @@ protected AbstractDeserializer(BeanDescription beanDesc)
7795
* @since 2.9
7896
*/
7997
protected AbstractDeserializer(AbstractDeserializer base,
80-
ObjectIdReader objectIdReader)
98+
ObjectIdReader objectIdReader, Map<String, SettableBeanProperty> props)
8199
{
82100
_baseType = base._baseType;
83101
_backRefProperties = base._backRefProperties;
@@ -87,8 +105,9 @@ protected AbstractDeserializer(AbstractDeserializer base,
87105
_acceptDouble = base._acceptDouble;
88106

89107
_objectIdReader = objectIdReader;
108+
_properties = props;
90109
}
91-
110+
92111
/**
93112
* Factory method used when constructing instances for non-POJO types, like
94113
* {@link java.util.Map}s.
@@ -109,30 +128,49 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
109128
if (accessor != null) {
110129
ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
111130
if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory)
131+
JavaType idType;
132+
ObjectIdGenerator<?> idGen;
133+
SettableBeanProperty idProp = null;
134+
ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
135+
112136
// 2.1: allow modifications by "id ref" annotations as well:
113137
objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo);
114138
Class<?> implClass = objectIdInfo.getGeneratorType();
115-
// 02-May-2017, tatu: Alas, properties are NOT available for abstract classes; can not
116-
// support this particular type. Yet.
139+
117140
if (implClass == ObjectIdGenerators.PropertyGenerator.class) {
118-
ctxt.reportBadDefinition(_baseType, String.format(
141+
PropertyName propName = objectIdInfo.getPropertyName();
142+
idProp = (_properties == null) ? null : _properties.get(propName.getSimpleName());
143+
if (idProp == null) {
144+
ctxt.reportBadDefinition(_baseType, String.format(
145+
"Invalid Object Id definition for %s: can not find property with name '%s'",
146+
handledType().getName(), propName));
147+
}
148+
idType = idProp.getType();
149+
idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
150+
/*
151+
ctxt.reportBadDefinition(_baseType, String.format(
152+
/
119153
"Invalid Object Id definition for abstract type %s: can not use `PropertyGenerator` on polymorphic types using property annotation",
120154
handledType().getName()));
155+
*/
156+
} else { // other types simpler
157+
resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
158+
JavaType type = ctxt.constructType(implClass);
159+
idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
160+
idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo);
121161
}
122-
ObjectIdResolver resolver = ctxt.objectIdResolverInstance(accessor, objectIdInfo);
123-
JavaType type = ctxt.constructType(implClass);
124-
JavaType idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
125-
SettableBeanProperty idProp = null;
126-
ObjectIdGenerator<?> idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo);
127162
JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
128163
ObjectIdReader oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(),
129164
idGen, deser, idProp, resolver);
130-
return new AbstractDeserializer(this, oir);
165+
return new AbstractDeserializer(this, oir, null);
131166
}
132167
}
133168
}
134-
// either way, need to resolve serializer:
135-
return this;
169+
if (_properties == null) {
170+
return this;
171+
}
172+
// Need to ensure properties are dropped at this point, regardless
173+
return new AbstractDeserializer(this, _objectIdReader, null);
136174
}
137175

138176
/*

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -666,9 +666,8 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
666666

667667
// First: may have an override for Object Id:
668668
final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
669-
final AnnotatedMember accessor = (property == null || intr == null)
670-
? null : property.getMember();
671-
if (_neitherNull(accessor, intr)) {
669+
final AnnotatedMember accessor = _neitherNull(property, intr) ? property.getMember() : null;
670+
if (accessor != null) {
672671
ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
673672
if (objectIdInfo != null) { // some code duplication here as well (from BeanDeserializerFactory)
674673
// 2.1: allow modifications by "id ref" annotations as well:
@@ -690,7 +689,7 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
690689
}
691690
idType = idProp.getType();
692691
idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
693-
} else { // other types need to be simpler
692+
} else { // other types are to be simpler
694693
JavaType type = ctxt.constructType(implClass);
695694
idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
696695
idProp = null;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ public JsonDeserializer<?> build()
380380
* @since 2.0
381381
*/
382382
public AbstractDeserializer buildAbstract() {
383-
return new AbstractDeserializer(this, _beanDesc, _backRefProperties);
383+
return new AbstractDeserializer(this, _beanDesc, _backRefProperties, _properties);
384384
}
385385

386386
/**

src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,8 +490,9 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,
490490

491491
for (int i = 0, len = _props.length; ; ++i) {
492492
if (i == len) {
493-
throw new IllegalArgumentException("Invalid Object Id definition for "+_handledType.getName()
494-
+": can not find property with name '"+propName+"'");
493+
provider.reportBadDefinition(_beanType, String.format(
494+
"Invalid Object Id definition for %s: can not find property with name '%s'",
495+
handledType().getName(), propName));
495496
}
496497
BeanPropertyWriter prop = _props[i];
497498
if (propName.equals(prop.getName())) {

src/test/java/com/fasterxml/jackson/failing/PolymorphicWithObjectId1551Test.java

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ static class VehicleOwnerViaProp {
2525
public Vehicle ownedVehicle;
2626
}
2727

28-
public void testWithAbstractUsingProp() throws Exception {
28+
static class VehicleOwnerBroken {
29+
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "bogus")
30+
@JsonIdentityReference(alwaysAsId = false)
31+
public Vehicle ownedVehicle;
32+
}
33+
34+
public void testWithAbstractUsingProp() throws Exception
35+
{
2936
Car c = new Car();
3037
c.vehicleId = "123";
3138
c.numberOfDoors = 2;
@@ -38,18 +45,47 @@ public void testWithAbstractUsingProp() throws Exception {
3845
ObjectMapper objectMapper = new ObjectMapper();
3946
String serialized = objectMapper.writer()
4047
.writeValueAsString(new VehicleOwnerViaProp[] { v1, v2 });
41-
4248
// 02-May-2017, tatu: Not possible to support as of Jackson 2.8 at least, so:
4349

50+
VehicleOwnerViaProp[] deserialized = objectMapper.readValue(serialized, VehicleOwnerViaProp[].class);
51+
assertEquals(2, deserialized.length);
52+
assertSame(deserialized[0].ownedVehicle, deserialized[1].ownedVehicle);
53+
}
54+
55+
public void testFailingAbstractUsingProp() throws Exception
56+
{
57+
Car c = new Car();
58+
c.vehicleId = "123";
59+
c.numberOfDoors = 2;
60+
// both owners own the same vehicle (car sharing ;-))
61+
VehicleOwnerBroken v1 = new VehicleOwnerBroken();
62+
v1.ownedVehicle = c;
63+
VehicleOwnerBroken v2 = new VehicleOwnerBroken();
64+
v2.ownedVehicle = c;
65+
66+
ObjectMapper objectMapper = new ObjectMapper();
67+
try {
68+
objectMapper.writer()
69+
.writeValueAsString(new VehicleOwnerBroken[] { v1, v2 });
70+
} catch (InvalidDefinitionException e) {
71+
// on serialization, reported for different type
72+
assertEquals(Car.class, e.getType().getRawClass());
73+
verifyException(e, "Invalid Object Id definition");
74+
verifyException(e, "can not find property with name 'bogus'");
75+
}
76+
77+
// and same for deser
78+
final String JSON = aposToQuotes(
79+
"[{'ownedVehicle':{'@class':'com.fasterxml.jackson.failing.PolymorphicWithObjectId1551Test$Car','vehicleId':'123',"
80+
+"'numberOfDoors':2}},{'ownedVehicle':'123'}]"
81+
);
4482
try {
45-
/*VehicleOwnerViaProp[] deserialized = */
46-
objectMapper.readValue(serialized, VehicleOwnerViaProp[].class);
83+
objectMapper.readValue(JSON, VehicleOwnerBroken[].class);
4784
fail("Should not pass");
4885
} catch (InvalidDefinitionException e) {
4986
assertEquals(Vehicle.class, e.getType().getRawClass());
50-
verifyException(e, "Invalid Object Id definition for abstract type");
87+
verifyException(e, "Invalid Object Id definition");
88+
verifyException(e, "can not find property with name 'bogus'");
5189
}
52-
// assertEquals(2, deserialized.length);
53-
// assertSame(deserialized[0].ownedVehicle, deserialized[1].ownedVehicle);
5490
}
5591
}

0 commit comments

Comments
 (0)