-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Search before asking
- I searched in the issues and found nothing similar.
Describe the bug
From #4756:
Test case:
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonMerge;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.json.JsonMapper;
// dummy interface to "confuse" type resolution
interface MyList<T> extends List<T> {}
class MergeList {
// dummy implementation of MyList that Jackson shouldn't care about with a black-box creator
private static class MyArrayList<T> extends ArrayList<T> implements MyList<T> {}
static <T> MyList<T> create() {
return new MyArrayList<T>();
}
@JsonMerge
@JsonProperty
MyList<String> values = create();
{
values.add("a");
}
public static void main(String[] args) throws Exception {
JsonMapper MAPPER = JsonMapper.builder().build();
MergeList mergeList = new MergeList();
var string = MAPPER.writeValueAsString(mergeList);
System.out.println(string);
MergeList w = MAPPER.readValue(("{\"values\":[\"x\"]}"), MergeList.class);
System.out.println(w.values);
}
}
Throws
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a deserializer for non-concrete Collection type [collection type; class example.MyList, contains [simple type, class java.lang.String]]
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1888)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:321)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:284)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:174)
at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:659)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:552)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:347)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:284)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:174)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:669)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:5048)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4918)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3860)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3828)
at example.MergeList.main(MergeList.java:33)
If I understood @JsonMerge
correctly, it should have resolved correctly:
merging meaning that the current value is first accessed (with a getter or field) and then modified with incoming data. If merging is not used assignment happens without considering current state.
...
For Collection and Arrays, merging is done by appending incoming data into contents of existing Collection/array
But the error is that Jackson cannot find a deserializer for MyList
. It doesn't need to because it can access the current value (with a getter or @JsonProperty
as in this example) and modify it. Without @JsonMerge
, the current state of the values
field is not considered and so I expect exactly this kind of failure.
Perhaps I misunderstood @JsonMerge
, but the docs are quite clear I think.
A workaround is possible via a custom setter:
private void setMyList(List<Integer> ints) { ints.forEach(MyList::add); }
But this "pollutes" the class and also requires opens
declarations in module-info
to be able to read the private
setter.
Version Information
2.17 and 2.18