Jackson-databind: No way to make 2.9 behave the same as 2.8 with null values in JsonDynamicSetter/JsonDynamicGetter

Created on 3 Nov 2017  路  2Comments  路  Source: FasterXML/jackson-databind

With om.setSerializationInclusion(Include.NON_NULL), in 2.8, null values in dynamic data were kept. in 2.9, however, only top-level values are kept (a in the example code, whereasc2` is missing).

Tried several configuration switches of ObjectMapper, but I don't think there's a way to keep the same behaviour as before.

public class Jackson29DynamicDataNullTest {
    @Test
    public void testNull() throws IOException {
        String json = "{'a': null, 'b': 2, 'c': { 'c1': 3, 'c2': null } }".replace('\'', '"');
        Map<String, Object> data = om().readValue(json, Map.class);
        assertTrue(data.containsKey("a"));
        assertNull(data.get("a"));
        assertTrue(((Map) data.get("c")).containsKey("c2"));
        assertNull(((Map) data.get("c")).get("c2"));
        Person p = new Person();
        p.data = data;
        p.name = "Pep";
        p.age = null;

        String json2 = om().writeValueAsString(p);
        System.err.println(json2);
        ObjectNode on = om().readValue(json2, ObjectNode.class);
        assertTrue(on.has("a"));
        assertTrue(on.get("a").isNull());
        assertTrue(on.get("c").has("c2"));
        assertTrue(on.get("c").get("c2").isNull());
    }

    ObjectMapper om() {
        ObjectMapper om = new ObjectMapper();
        om.setSerializationInclusion(Include.NON_NULL);

        // NONE OF THESE SWITCHES WORKED
        // om.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, true);
        // om.configOverride(Map.class)
        // .setInclude(JsonInclude.Value.construct(JsonInclude.Include.ALWAYS,
        // JsonInclude.Include.USE_DEFAULTS));
        return om;
    }

    static class Person {
        Map<String, Object> data;
        String name;
        Integer age;

        @JsonAnyGetter
        public Map<String, Object> getData() {
            return data;
        }

        @JsonAnySetter
        public void setData(Map<String, Object> data) {
            this.data = data;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

    }

}

Most helpful comment

Have you tried method setDefaultPropertyInclusion() of ObjectMapper?
Discrepancy is probably due to setSerializationInclusion now being a shortcut for:

    public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) {
        return setDefaultPropertyInclusion(JsonInclude.Value.construct(incl, incl));
    }

that is, using same value for both "value" (container) and contents (element(s) within).
I think earlier definition is more likely to be along lines of:

        return setDefaultPropertyInclusion(JsonInclude.Value.construct(incl, JsonInclude.ALWAYS));

which in retrospect would have likely been better implementation.
But unfortunately none of unit tests were testing for particular behavioral difference for this usage.

Note that Include.USE_DEFAULTS would have no effect for contents; you probably want ALWAYS instead. It can be set either as global defaults, or as override for Map.

All 2 comments

Have you tried method setDefaultPropertyInclusion() of ObjectMapper?
Discrepancy is probably due to setSerializationInclusion now being a shortcut for:

    public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) {
        return setDefaultPropertyInclusion(JsonInclude.Value.construct(incl, incl));
    }

that is, using same value for both "value" (container) and contents (element(s) within).
I think earlier definition is more likely to be along lines of:

        return setDefaultPropertyInclusion(JsonInclude.Value.construct(incl, JsonInclude.ALWAYS));

which in retrospect would have likely been better implementation.
But unfortunately none of unit tests were testing for particular behavioral difference for this usage.

Note that Include.USE_DEFAULTS would have no effect for contents; you probably want ALWAYS instead. It can be set either as global defaults, or as override for Map.

Yes,

        om.setDefaultPropertyInclusion(JsonInclude.Value.construct(Include.NON_NULL, Include.ALWAYS));

works as expected, thanks!聽

Was this page helpful?
0 / 5 - 0 ratings