Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jackson - skip keys with null values while serializing the HashMap object

I'm trying to serialize the Java object with hashmap into json string using jackson object mapper.

Below is the class definition of Ext -

        import com.fasterxml.jackson.annotation.JsonAnyGetter;
        import com.fasterxml.jackson.annotation.JsonAnySetter;
        import com.fasterxml.jackson.annotation.JsonIgnore;
        import com.fasterxml.jackson.annotation.JsonInclude;
        import com.fasterxml.jackson.annotation.JsonPropertyOrder;
        import com.fasterxml.jackson.annotation.JsonInclude.Include;
        import java.io.Serializable;
        import java.util.HashMap;
        import java.util.Map;
        import java.util.Objects;

        @JsonInclude(Include.NON_NULL)
        @JsonPropertyOrder({})
        public class Ext implements Serializable {

            @JsonIgnore
            private Map<String, Object> additionalProperties = new HashMap();
            private static final long serialVersionUID = -4500317258794294335L;

            public Ext() {
            }

            @JsonAnyGetter
            public Map<String, Object> getAdditionalProperties() {
                return this.additionalProperties;
            }

            @JsonAnySetter
            public void setAdditionalProperty(String name, Object value) {
                this.additionalProperties.put(name, value);
            }

            // ignore toString, equals and hascode

            public static class ExtBuilder {
                protected Ext instance;

                public ExtBuilder() {
                    if (this.getClass().equals(Ext.ExtBuilder.class)) {
                        this.instance = new Ext();
                    }
                }

                public Ext build() {
                    Ext result = this.instance;
                    this.instance = null;
                    return result;
                }

                public Ext.ExtBuilder withAdditionalProperty(String name, Object value) {
                    this.instance.getAdditionalProperties().put(name, value);
                    return this;
                }
            }
        }

Below is the the sample test case -

    @Test
    public void testNullObjectSerialization() throws Exception {

        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
        mapper.setDefaultPropertyInclusion(
              JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL));

        ByteArrayOutputStream out = new ByteArrayOutputStream(1000);
        mapper.writeValue(out, new Ext.ExtBuilder().withAdditionalProperty("unexpected", null).withAdditionalProperty("expected", true).build());
        String result = new String(out.toByteArray());
        Assert.assertEquals("{\"expected\":true}", result);
    }

I used

mapper.setDefaultPropertyInclusion(
              JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL));` 

by using the answer provided in stack overflow question.

I'm expecting the result as {"expected":true} but somehow the key with null value is included in the result.

How can I fix this issue?

Note:

  1. Code is on github here.
  2. I'm using jacksersion version 2.9.8
like image 518
Rajkumar Natarajan Avatar asked May 22 '26 11:05

Rajkumar Natarajan


1 Answers

When you put @JsonInclude on a field, that means if the field (not the element of fields) is null then it will be ignored from conversion.

  • In your case when you use @JsonInclude on additionalProperties that means, if additionalProperties(itself) is null then it will be ignored from conversion.

so @JsonInclude only checks additionalProperties itself for null value, not its elements.


Example: Assume that you have an Ext object with additionalProperties=null, when you want to serialize it, the additionalProperties is ignored because it is null.

  • But in your scenario the elements of the additionalProperties map contain null and Jackson doesn't ignore them.

Solution 1

You can serialize additionalProperties map (itself) not the whole Ext object.

public static void main(String[] args) throws JsonProcessingException {

        //Create Ext  Object
        Ext ext = new Ext.ExtBuilder().withAdditionalProperty("unexpected", null).withAdditionalProperty("expected", true).build();

        //Config
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        //Conversion the additionalProperties map (ext.getAdditionalProperties())
        String filterMap = mapper.writeValueAsString(ext.getAdditionalProperties());

        //Result ({"expected":true})
        System.out.println(filterMap);


    }
}        

Convert JSON to Ext Object

And also you can get Ext object from the generated string. in this case, your object has a filtered additionalProperties (does not have any element with null key or null value)

public static void main(String[] args) throws JsonProcessingException {

        //Create Ext  Object
        Ext ext = new Ext.ExtBuilder().withAdditionalProperty("unexpected", null).withAdditionalProperty("expected", true).build();

        //Config
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        //Conversion the additionalProperties map (ext.getAdditionalProperties())
        String filterMap = mapper.writeValueAsString(ext.getAdditionalProperties());

        //Result ({"expected":true})
        System.out.println(filterMap);

        //Convert back JSON string to Ext object
        Ext filterdExt = mapper.readValue(filterMap, Ext.class);

        //Print AdditionalProperties of filterdExt ({"expected":true})
        System.out.println(filterdExt.getAdditionalProperties());

    }

Solution 2

You can write a custom serializer.

This link might be useful for this purpose

like image 106
Mehrdad HosseinNejad Yami Avatar answered May 23 '26 23:05

Mehrdad HosseinNejad Yami