Fix #1051
diff --git a/release-notes/VERSION b/release-notes/VERSION
index f56daf0..00320ff 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -4,6 +4,10 @@
=== Releases ===
------------------------------------------------------------------------
+2.5.5-1 (not released)
+
+#1051: Problem with Object Id and Type Id as Wrapper Object (regression in 2.5.1)
+
2.5.5 (07-Dec-2015)
#844: Using JsonCreator still causes invalid path references in JsonMappingException
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
index 6569b05..659762b 100644
--- a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
@@ -76,45 +76,46 @@
* deserialization.
*/
@SuppressWarnings("resource")
- private final Object _deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+ private final Object _deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
// 02-Aug-2013, tatu: May need to use native type ids
- if (jp.canReadTypeId()) {
- Object typeId = jp.getTypeId();
+ if (p.canReadTypeId()) {
+ Object typeId = p.getTypeId();
if (typeId != null) {
- return _deserializeWithNativeTypeId(jp, ctxt, typeId);
+ return _deserializeWithNativeTypeId(p, ctxt, typeId);
}
}
-
// first, sanity checks
- if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
- throw ctxt.wrongTokenException(jp, JsonToken.START_OBJECT,
+ JsonToken t = p.getCurrentToken();
+ if (t == JsonToken.START_OBJECT) {
+ // should always get field name, but just in case...
+ if (p.nextToken() != JsonToken.FIELD_NAME) {
+ throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME,
+ "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
+ }
+ } else if (t != JsonToken.FIELD_NAME) {
+ throw ctxt.wrongTokenException(p, JsonToken.START_OBJECT,
"need JSON Object to contain As.WRAPPER_OBJECT type information for class "+baseTypeName());
}
- // should always get field name, but just in case...
- if (jp.nextToken() != JsonToken.FIELD_NAME) {
- throw ctxt.wrongTokenException(jp, JsonToken.FIELD_NAME,
- "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
- }
- final String typeId = jp.getText();
+ final String typeId = p.getText();
JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId);
- jp.nextToken();
+ p.nextToken();
// Minor complication: we may need to merge type id in?
- if (_typeIdVisible && jp.getCurrentToken() == JsonToken.START_OBJECT) {
+ if (_typeIdVisible && p.getCurrentToken() == JsonToken.START_OBJECT) {
// but what if there's nowhere to add it in? Error? Or skip? For now, skip.
TokenBuffer tb = new TokenBuffer(null, false);
tb.writeStartObject(); // recreate START_OBJECT
tb.writeFieldName(_typePropertyName);
tb.writeString(typeId);
- jp = JsonParserSequence.createFlattened(tb.asParser(jp), jp);
- jp.nextToken();
+ p = JsonParserSequence.createFlattened(tb.asParser(p), p);
+ p.nextToken();
}
- Object value = deser.deserialize(jp, ctxt);
+ Object value = deser.deserialize(p, ctxt);
// And then need the closing END_OBJECT
- if (jp.nextToken() != JsonToken.END_OBJECT) {
- throw ctxt.wrongTokenException(jp, JsonToken.END_OBJECT,
+ if (p.nextToken() != JsonToken.END_OBJECT) {
+ throw ctxt.wrongTokenException(p, JsonToken.END_OBJECT,
"expected closing END_OBJECT after type information and deserialized value");
}
return value;
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/WrapperObjectWithObjectIdTest.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/WrapperObjectWithObjectIdTest.java
new file mode 100644
index 0000000..4000b69
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/WrapperObjectWithObjectIdTest.java
@@ -0,0 +1,88 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+// Test for [databind#1051], issue with combination of Type and Object ids,
+// if (but only if) `JsonTypeInfo.As.WRAPPER_OBJECT` used.
+public class WrapperObjectWithObjectIdTest extends BaseMapTest
+{
+ @JsonRootName(value = "company")
+ static class Company {
+ public List<Computer> computers;
+
+ public Company() {
+ computers = new ArrayList<Computer>();
+ }
+
+ public Company addComputer(Computer computer) {
+ if (computers == null) {
+ computers = new ArrayList<Computer>();
+ }
+ computers.add(computer);
+ return this;
+ }
+ }
+
+ @JsonIdentityInfo(
+ generator = ObjectIdGenerators.PropertyGenerator.class,
+ property = "id"
+ )
+ @JsonTypeInfo(
+ use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.WRAPPER_OBJECT,
+ property = "type"
+ )
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = DesktopComputer.class, name = "desktop"),
+ @JsonSubTypes.Type(value = LaptopComputer.class, name = "laptop")
+ })
+ static class Computer {
+ public String id;
+ }
+
+ @JsonTypeName("desktop")
+ static class DesktopComputer extends Computer {
+ public String location;
+
+ protected DesktopComputer() { }
+ public DesktopComputer(String id, String loc) {
+ this.id = id;
+ location = loc;
+ }
+ }
+
+ @JsonTypeName("laptop")
+ static class LaptopComputer extends Computer {
+ public String vendor;
+
+ protected LaptopComputer() { }
+ public LaptopComputer(String id, String v) {
+ this.id = id;
+ vendor = v;
+ }
+ }
+
+ public void testSimple() throws Exception
+ {
+ Company comp = new Company();
+ comp.addComputer(new DesktopComputer("computer-1", "Bangkok"));
+ comp.addComputer(new DesktopComputer("computer-2", "Pattaya"));
+ comp.addComputer(new LaptopComputer("computer-3", "Apple"));
+
+ final ObjectMapper mapper = new ObjectMapper();
+
+ String json = mapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(comp);
+
+ System.out.println("JSON: "+json);
+
+ Company result = mapper.readValue(json, Company.class);
+ assertNotNull(result);
+ assertNotNull(result.computers);
+ assertEquals(3, result.computers.size());
+ }
+}