jackson-databind
jackson-databind copied to clipboard
Nulls.AS_EMPTY doesn't work for collections containing records
Describe the bug
When deserializating an empty JSON object {}
into a record that contains a list of another record results in:
java.lang.IllegalStateException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot create empty instance of [collection type; class java.util.List, contains [simple type, class com.example.Test$Foo$Bar]], no default Creator
at [Source: (String)"{}"; line: 1, column: 1]
Version information 2.12.2
To Reproduce
record Foo(List<Bar> list) {
record Bar(String name, String value) {}
}
@Test
public void test() {
ObjectMapper mapper = new ObjectMapper().setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY));
try {
mapper.readValue("{}", Foo.class);
} catch (JsonProcessingException e) {
throw new IllegalStateException(e);
}
}
The above fails with:
java.lang.IllegalStateException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot create empty instance of [collection type; class java.util.List, contains [simple type, class com.example.Test$Foo$Bar]], no default Creator
at [Source: (String)"{}"; line: 1, column: 1]
Expected behavior I would expect the deserialization give me an empty list.
Any news on this? I recently just ran into the same issue
@krzyk maybe should be using .forValueNulls(Nulls.AS_EMPTY)
instead of .forContentNulls(Nulls.AS_EMPTY)
?
Otherwise, can you please provide an example for JavaBeans (i.e. not Records) that actually works with .forContentNulls
?
@yihtserns You are correct: content nulls are only for Map
entries and array/Collection
elements; not for POJO/Record properties.
But the exception is strange at any rate: it should not be a problem to create empty List
... (it gets mapped to ArrayList
by default, uses default constructor).
But the exception is strange at any rate: it should not be a problem to create empty List...
@cowtowncoder seems like it is complaining that Bar
does not have no-arg constructor:
simple type, class com.example.Test$Foo$Bar
...although the error message does sound misleading. Same behaviour can be observed when using an equivalent non-Records class:
public static class Foo {
private List<Bar> list;
@JsonCreator
public Foo(@JsonProperty("list") List<Bar> list) {
this.list = list;
}
public static class Bar {
private String name;
private String value;
@JsonCreator
public Bar(@JsonProperty("name") String name, @JsonProperty("value") String value) {
this.name = name;
this.value = value;
}
}
}
The issue goes away by adding a no-arg constructor to Bar
:
public static class Bar {
private String name;
private String value;
@JsonCreator
public Bar(@JsonProperty("name") String name, @JsonProperty("value") String value) {
this.name = name;
this.value = value;
}
public Bar() { // No more issue with this
}
}
~~Unfortunately, adding a no-arg constructor (with/without @JsonCreator
) for the Bar
record class won't fix the issue (although it can work on #3724's branch).~~ UPDATE: Starting from 2.15
(due to #3724), adding no-arg constructor for the Bar
record class will make the error go away.
Ah. Ok, so the issue here is specifically that due to setting that would use "As.EMPTY" for elements of List
, and since nominal type of List
property is one that does not have zero-args constructor, things fail.
This because in the event we did need "empty" Bar
instance we'd have no way to construct one.
That is sort of valid reason to fail, potentially, if (but only if?) we ever needed to have empty Bar
instance.
Which here we don't.
I think there's an older issue mentioning the same problem wrt (overly) eager construction of handler to provide "empty" instance. But there is also then the question of deferring failure to a later point -- is that always desireable? Sometimes it'd be good to know about potential problem during construction of deserializer and not wait until specific piece of data triggers the condition.
For now it'd make sense to change configuration to use .forValueNulls(Nulls.AS_EMPTY)
, not .forContentNulls(...)
.
Worth noting, too, is that @JsonSetter(...)
annotation can be added to the property to override setting for List
property as well.