jackson-databind: Nested type arguments doesn't work with polymorphic types
When using @JsonSubTypes/@JsonTypeInfo on an interface with a generic parameter (de)serialising a subclass that wraps it’s own generic parameter in another generic class or interface doesn’t work.
It’s a bit complex to explain with words so here’s a short example:
public class GenericPolymorphismTest {
@JsonTypeInfo(property = "type", use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({
@JsonSubTypes.Type(value = AddToList.class, name = "addToList")
})
public interface Operation<T> {
T perform(T operand);
}
public static class AddToList<T> implements Operation<List<T>> {
T value;
@Override
public List<T> perform(List<T> operand) {
operand.add(value);
return operand;
}
}
public static class Container {
Operation<List<UUID>> listOperation;
}
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.setVisibility(new VisibilityChecker.Std(JsonAutoDetect.Visibility.ANY));
}
public static void main(String[] args) throws JsonProcessingException {
AddToList<UUID> addToList = new AddToList<>();
addToList.value = UUID.randomUUID();
Container container = new Container();
container.listOperation = addToList;
System.out.println(objectMapper.writeValueAsString(container));
}
}
This crashes with the following exception:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Class java.util.UUID not subtype of [collection type; class java.util.List, contains [simple type, class java.util.UUID]] (through reference chain: GenericPolymorphismTest$Container["listOperation"]->GenericPolymorphismTest$AddToList["value"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:388)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:348)
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:343)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:698)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeWithType(BeanSerializerBase.java:581)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:706)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3681)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3057)
at GenericPolymorphismTest.main(GenericPolymorphismTest.java:57)
Caused by: java.lang.IllegalArgumentException: Class java.util.UUID not subtype of [collection type; class java.util.List, contains [simple type, class java.util.UUID]]
at com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:359)
at com.fasterxml.jackson.databind.cfg.MapperConfig.constructSpecializedType(MapperConfig.java:306)
at com.fasterxml.jackson.databind.DatabindContext.constructSpecializedType(DatabindContext.java:149)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._findAndAddDynamic(BeanPropertyWriter.java:870)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:682)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)
... 8 more
If I try work around it by making value
into a List<T>
deserialisation works but serialisation gives me a List<String>
instead of List<UUID>
so using a list after perform
will throw ClassCastException
s.
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Comments: 25 (12 by maintainers)
Commits related to this issue
- Add a failing test for #1604 — committed to FasterXML/jackson-databind by cowtowncoder 7 years ago
- Fix #1604 for 2.8.11 — committed to epollan/jackson-databind by epollan 7 years ago
- backport #1604 test to 2.8 — committed to FasterXML/jackson-databind by cowtowncoder 7 years ago
- Add more test cases for #1604 — committed to FasterXML/jackson-databind by cowtowncoder 7 years ago
- Fix an issue with new #1604 tests; was not exercising actual problem — committed to FasterXML/jackson-databind by cowtowncoder 7 years ago
- More type fixes wrt #1604 verification — committed to FasterXML/jackson-databind by cowtowncoder 7 years ago
- Fix #1604 — committed to FasterXML/jackson-databind by cowtowncoder 7 years ago
@dnno Could you please file a new issue for this: even if the problem is related to this one (caused by fix), it is easier to track fix that way. You can add a reference to this issue as likely root cause.
I can’t say much except that this could be related to / same as #1964, which will be fixed for
2.9.6
Yes. So, just to clarify what I meant: existing type resolution works fine, as is, when only resolving a single type. What is needed here is sort of re-parent things (grafting?), and it could either be implemented as separate recursive process (instead of resolving it is actually walking existing type hierarchy, really), or trying to retrofit resolution with this.
There are further challenges in that type objects are (mostly, ideally should be fully) immutable, to figure out what and how to copy.
Yes, I can reproduce this. Looks like code path may be slightly different, but at least there’s something to dig into.