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

When Include.NON_DEFAULT setting is used, isEmpty() method is not called on the serializer #4464

Closed
1 task done
teodord opened this issue Apr 1, 2024 · 10 comments · Fixed by #4467
Closed
1 task done
Labels
Milestone

Comments

@teodord
Copy link

teodord commented Apr 1, 2024

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

Documentation says that when Include.NON_DEFAULT is used, null, empty and default values of a property are skipped on serialization. It is not clear how the empty check is done because the isEmpty method of the serializer is called only when Include.NON_EMPTY is used explicitly.

I have a case where I set Include.NON_DEFAULT globally on the ObjectMapper, and I have implemented a custom serializer to override the isEmpty method and thus let Jackson know when my object is empty. But my custom implementation of the isEmpty method is not called.
The only way to have it called by Jackson is to explicitly override the property inclusion configuration and use NON_EMPTY instead of the global NON_DEFAULT.

Version Information

2.15.3

Reproduction

<-- Any of the following

  1. Brief code sample/snippet: include here in preformatted/code section
  2. Longer example stored somewhere else (diff repo, snippet), add a link
  3. Textual explanation: include here
    -->
// Your code here

Expected behavior

No response

Additional context

No response

@teodord teodord added the to-evaluate Issue that has been received but not yet evaluated label Apr 1, 2024
@JooHyukKim
Copy link
Member

Could you provide simple reproduction, with Mapper configuration, class declarations and such?

I tried writing a reproduction as below, but it seems to behave differently than the decription above.

    public static class Foo {
        public String getFoo() {
            return "";
        }
    }

    @Test
    public void test86() throws IOException {
        ObjectMapper mapper = JsonMapper.builder()
                .serializationInclusion(JsonInclude.Include.NON_DEFAULT)
                .build();
        String json = mapper.writeValueAsString(new Foo());
        assertEquals("{}", json);
    }
    ```

@teodord
Copy link
Author

teodord commented Apr 2, 2024

I probably did not make myself clear, but my case is about a custom serializer overriding the isEmpty method, which does not get called when NON_DEFAULT is used. It gets called only when NON_EMPTY is used, although documentation says NON_DEFAULT also excludes empty values.

I attached a small Maven project demonstrating the issue. It works if we uncomment the NON_EMPTY annotation in Foo.java at line 9.

jackson-test.zip

@JooHyukKim
Copy link
Member

Documentation says that when Include.NON_DEFAULT is used, null, empty and default values of a property are skipped on serialization.

Hmmm, the documentation is somewhat context-driven, meaning the behavior varies depending on context, POJO vs non-POJO, etc....

Could you check again, if it really matches your case? I attached the documenation.

image

@JooHyukKim
Copy link
Member

JooHyukKim commented Apr 2, 2024

From what I read the documenation, one thing for sure is that NON_DEFAULT is not a superset of NON_EMPTY or vice versa.~~ ~~How about using NON_EMPTY` if possible? It seems more straight forward 🤔?

@teodord
Copy link
Author

teodord commented Apr 2, 2024

I am not sure why you say NON_DEFAULT is not a superset of NON_EMPTY. In the documentation snippet you posted above, I am clearly in this case:
image
I have set the NON_DEFAULT globally, on the ObjectMapper and so I qualify for this case.
First bullet says: "All Values considerer empty as per NON_EMPTY are excluded". So it thus confirms NON_DEFAULT is a superset of NON_EMPTY.
What do I get wrong here?
Furthermore, your own test showed that when NON_DEFAULT is used globally, empty strings are excluded.
What does not work is when custom serializer is used.

@JooHyukKim
Copy link
Member

JooHyukKim commented Apr 2, 2024

Furthermore, your own test showed that when NON_DEFAULT is used globally, empty strings are excluded.
What does not work is when custom serializer is used.

Thank you for pointing out. I sort of drifted to a wrong point.

You are right that isEmpty() is not called with custom serializer.

@teodord
Copy link
Author

teodord commented Apr 2, 2024

Thank you for confirming, but is this a bug or it was meant to work this way and documentation is faulty or incomplete?

@JooHyukKim
Copy link
Member

JooHyukKim commented Apr 2, 2024

Bug most likely.
But not sure when the fix will be, or which version.

@JooHyukKim
Copy link
Member

Here is the missing reproduction

public class Test4464Test {
    public static class BarSerializer extends StdScalarSerializer<Bar> {
        
        public BarSerializer() {
            super((Class<Bar>) null);
        }

        @Override
        public void serialize(Bar value, JsonGenerator gen, SerializerProvider provider) throws IOException {
            gen.writeObject(value);
        }

        @Override
        public boolean isEmpty(SerializerProvider provider, Bar value) {
            return Bar.I_AM_EMPTY.equals(value.getName());
        }
    }

    public static class Bar {
        public static final String I_AM_EMPTY = "I_AM_EMPTY";

        public String getName() {
            return I_AM_EMPTY;
        }
    }

    public static class Foo {
        //@JsonInclude(Include.NON_EMPTY)
        @JsonSerialize(using = BarSerializer.class)
        public Bar getBar() {
            return new Bar();
        }
    }

    @Test
    public void test86() throws IOException {
        ObjectMapper mapper = JsonMapper.builder().serializationInclusion(JsonInclude.Include.NON_DEFAULT).build();
        String json = mapper.writeValueAsString(new Foo());
        assertEquals("{}", json);
    }
}

@cowtowncoder cowtowncoder changed the title When Include.NON_DEFAULT setting is used, isEmpty method is not called on the serializer When Include.NON_DEFAULT setting is used, isEmpty() method is not called on the serializer Apr 7, 2024
@cowtowncoder cowtowncoder added 2.18 and removed to-evaluate Issue that has been received but not yet evaluated labels Apr 7, 2024
@cowtowncoder cowtowncoder added this to the 2.18.0 milestone Apr 7, 2024
@cowtowncoder
Copy link
Member

Thank you @JooHyukKim -- merged for inclusion in 2.18.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants