Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nulls.AS_EMPTY doesn't work for collections containing values without default constructor (like Records) #3084

Open
krzyk opened this issue Mar 19, 2021 · 5 comments
Labels
Record Issue related to JDK17 java.lang.Record support

Comments

@krzyk
Copy link

krzyk commented Mar 19, 2021

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.

@krzyk krzyk added the to-evaluate Issue that has been received but not yet evaluated label Mar 19, 2021
@cowtowncoder cowtowncoder added the Record Issue related to JDK17 java.lang.Record support label Apr 11, 2021
@cowtowncoder cowtowncoder removed the to-evaluate Issue that has been received but not yet evaluated label Jul 15, 2021
@sagansfault
Copy link

Any news on this? I recently just ran into the same issue

@yihtserns
Copy link
Contributor

@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?

@cowtowncoder
Copy link
Member

@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).

@yihtserns
Copy link
Contributor

yihtserns commented Jan 11, 2023

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.

@cowtowncoder
Copy link
Member

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.

@cowtowncoder cowtowncoder changed the title Nulls.AS_EMPTY doesn't work for collections containing records Nulls.AS_EMPTY doesn't work for collections containing values without default constructor (like Records) Jan 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Record Issue related to JDK17 java.lang.Record support
Projects
None yet
Development

No branches or pull requests

4 participants