-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Closed
Milestone
Description
When a polymorphic interface has a subtype, and the subtype has @JsonFormat(shape=JsonFormat.Shape.ARRAY)
annotation and no fields, serializing its instance generates an empty object ({}
). However, parsing the interface tries to parse it as an array and fails.
Expected result is that it generates an empty array ([]
) instead of object so that parsing succeeds.
Here is a code to reproduce this issue:
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;
public class JacksonArrayTest
{
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.WRAPPER_ARRAY) // Both WRAPPER_OBJECT and WRAPPER_ARRAY cause the same problem
@JsonSubTypes({
@JsonSubTypes.Type(value = DirectLayout.class, name = "Direct"),
})
public interface Layout {
}
@JsonFormat(shape=JsonFormat.Shape.ARRAY)
public static class DirectLayout implements Layout {
}
public static void main(String[] args) throws Exception {
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(new DirectLayout());
System.out.println("Serialized JSON: " + json); //=> ["Direct",{}]. This is expected to be ["Direct",[]]
Layout instance = om.readValue(json, Layout.class);
System.out.println("Deserialized object class: " + instance.getClass()); //=> fails
}
}
Stacktrace of the failure is this:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize a POJO (of type JacksonArrayTest$DirectLayout) from non-Array representation (token: START_OBJECT): type/property designed to be serialized as JSON Array
at [Source: ["Direct",{}]; line: 1, column: 11]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
at com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.java:1234)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1122)
at com.fasterxml.jackson.databind.deser.impl.BeanAsArrayDeserializer._deserializeFromNonArray(BeanAsArrayDeserializer.java:352)
at com.fasterxml.jackson.databind.deser.impl.BeanAsArrayDeserializer.deserialize(BeanAsArrayDeserializer.java:97)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:116)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromObject(AsArrayTypeDeserializer.java:61)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:209)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:63)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2858)
at JacksonArrayTest.main(JacksonArrayTest.java:49)
This problem reproduces with Jackson 2.8.11 and 2.9.6.
This problem doesn't happen if the subtype has at least one field like this:
@JsonFormat(shape=JsonFormat.Shape.ARRAY)
public static class DirectLayout implements Layout {
public void setDummy(int v) {
}
public int getDummy() {
return 0;
}
}
public static void main(String[] args) throws Exception {
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(new DirectLayout());
System.out.println("Serialized JSON: " + json); //=> ["Direct",[0]]
Layout instance = om.readValue(json, Layout.class);
System.out.println("Deserialized object class: " + instance.getClass()); //=> class JacksonArrayTest$DirectLayout
}
A workaround is adding @JsonValue
method to the subtype as following:
@JsonFormat(shape=JsonFormat.Shape.ARRAY)
public static class DirectLayout implements Layout {
@JsonValue
@Deprecated
public List<Integer> toJson()
{
return new ArrayList<>();
}
}
public static void main(String[] args) throws Exception {
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(new DirectLayout());
System.out.println("Serialized JSON: " + json); //=> ["Direct",[]]. This is expected.
Layout instance = om.readValue(json, Layout.class);
System.out.println("Deserialized object class: " + instance.getClass()); //=> class JacksonArrayTest$DirectLayout
}
Metadata
Metadata
Assignees
Labels
No labels