Jackson JDK8 Data Type and Parameters Do Not Play Together

It seems to me that the Jackson JDK8 data type module sometimes ignores the Parameter Names module, which seems a bit surprising given that both require JDK8 and solve specific cases of using JDK8.

The problem here is that I could not find a way to make the JSON deserialization work without specifying the parameters explicitly (which should be the Parameter Names module). It also demonstrates this behavior only when trying to pass a specific JDK8 ( Optional<T>) type in the constructor of the container object (that is, it usually works, and I checked this). The code is compiled using the javac parameter -parameters.

The question is how to make it work, so that I can use the parameter name module (i.e. do not need to specify the value of annotation + in the constructor and determine its name by the name of the argument)?

I could be wrong and did not look at the code under the hood, so I would like to hear if there is something that I missed.

Consider this simple example.

Version package (all latest versions at the time of writing):

private val jacksonVer = "2.6.1"
private val jacksonCore: ModuleID = "com.fasterxml.jackson.core" % "jackson-core" % jacksonVer withSources() withJavadoc()
private val jacksonDataBind: ModuleID = "com.fasterxml.jackson.core" % "jackson-databind" % jacksonVer withSources() withJavadoc()
private val jacksonAnnotations: ModuleID = "com.fasterxml.jackson.core" % "jackson-annotations" % jacksonVer withSources() withJavadoc()
private val jacksonParamNames: ModuleID = "com.fasterxml.jackson.module" % "jackson-module-parameter-names" % "2.6.2" withSources() withJavadoc()
private val jacksonJdk8DataType: ModuleID = "com.fasterxml.jackson.datatype" % "jackson-datatype-jdk8" % "2.4.3" withSources() withJavadoc()

Container:

private static class SimpleTest {
    @JsonProperty private Optional<String> s1;
    @JsonProperty private Optional<String> s2;
    @JsonProperty private Map<String, String> map;

    private SimpleTest(@JsonProperty("s1") Optional<String> s1, @JsonProperty("s2") Optional<String> s2, @JsonProperty("map") Map<String, String> map) {
        this.s1 = s1;
        this.s2 = s2;
        this.map = map;
    }

    static SimpleTest of(Optional<String> s1, Optional<String> s2, Map<String, String> m) {
        return new SimpleTest(s1, s2, m);
    }
}

Serialization:

@Test
public void testSer() throws JsonProcessingException {
    SimpleTest test = SimpleTest.of(Optional.of("a"), Optional.empty(), Collections.emptyMap());
    System.out.println(JacksonUtil.getMapper().writeValueAsString(test));
}

Deserialization:

@Test
public void testDeser() throws IOException {
    String json = "{\n" +
            "  \"s1\" : \"a\",\n" +
            "  \"map\" : { }\n" +
            "}";
    JacksonUtil.getMapper().readValue(json, SimpleTest.class);
}

Running testSer()with such a container gives:

{
  "s1" : "a",
  "s2" : null,
  "map" : { }
}

Launch testDeser()using input like

{
  "s1" : "a",
  "map" : { }
}

also works and gives the expected results ( s1it matters, s2is Optional.emptyand mapempty), but only if the container constructor is defined above. I could not get it to work in the following combinations:
1)

private SimpleTest(Optional<String> s1, Optional<String> s2, Map<String, String> map) {...}

2)

private SimpleTest(@JsonProperty Optional<String> s1, @JsonProperty Optional<String> s2, @JsonProperty Map<String, String> map) {...}

, , - :

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com._3esi.load.bootstrap.ScratchPad$SimpleTest]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: {
  "s1" : "a",
  "map" : { }
}; line: 2, column: 3]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1106)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:294)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:131)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3731)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2724)

?

+4
2

, Jackson 2.6, : , @JsonCreator . , , , 2.7 ( , 2.6), .

@JsonCreator @JsonProperty, .

+3

CP Github:

:

public class OptionalTest {

    @Test
    public void shouldDeserialize() throws IOException {

        // given
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new Jdk8Module());
        objectMapper.registerModule(new ParameterNamesModule());

        // when
        String json = "{\"s1\":\"a\",\"map\":{}}";
        SimpleTest simpleTest = objectMapper.readValue(json, SimpleTest.class);

        then(simpleTest).isEqualToComparingFieldByField(new SimpleTest(Optional.of("a"), Optional.empty(), new HashMap<>()));
    }

    private static class SimpleTest {
        private Optional<String> s1;
        private Optional<String> s2;
        private Map<String, String> map;

        private SimpleTest(Optional<String> s1, Optional<String> s2, Map<String, String> map) {
            this.s1 = s1;
            this.s2 = s2;
            this.map = map;
        }

        static SimpleTest of(Optional<String> s1, Optional<String> s2, Map<String, String> m) {
            return new SimpleTest(s1, s2, m);
        }
    }
}

, jackson-parameter-name , 2.6.2.

+2

Source: https://habr.com/ru/post/1608561/


All Articles