4

Jackson JDK8 Data Type モジュールは、JDK8 を要求し、JDK8 に関する特定のユースケースを解決することを考えると、少し驚くべきように、パラメータ名モジュールを時々無視するようです。

ここでの問題は、パラメーター名を明示的に指定せずに JSON デシリアライゼーションを機能させる方法を見つけることができなかったことです (これは、パラメーター名モジュールがすべてであると想定されているものです)。また、コンテナー オブジェクト コンストラクターで JDK8 固有の型 ( Optional<T>) を渡そうとした場合にのみ、この動作が発生します (つまり、通常、これは機能し、私はそれをテストしました)。コードは javac パラメータでコンパイルされます-parameters

問題は、どのようにして Parameter Names モジュールを活用できるようにするか (つまり、コンストラクターで注釈 + 値を指定する必要がなく、引数名でプロパティ名を把握できるようにすること) です。

私は間違っているかもしれませんし、ボンネットの下のコードを見ていないので、見逃しているものがあれば教えてください.

この簡単な例を考えてみましょう。

バージョン スタック (この記事の執筆時点でのすべての最新バージョン):

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()

容器:

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);
    }
}

シリアライゼーション:

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

逆シリアル化:

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

testSer()このようなコンテナーで実行すると、次の結果が得られます。

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

testDeser()このような入力で実行する

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

も機能し、期待される結果 (s1s2Optional.emptyあり、map空である) が生成されますが、コンテナー コンストラクターが上記のように定義されている場合に限ります。次の組み合わせでは動作しませんでした: 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 に答える 2

4

これは、複数引数のコンストラクターの検出に関して、Jackson 2.6 に残っている問題が 1 つあるためだと思います。パラメーター名は検出されますが、コンストラクター自体は、@JsonCreator注釈を使用してマークしないと候補として保持されません。これは 2.7 で解決されることが望まれるものです (当初は 2.6 で修正される予定でした) が、当面は必要です。

コンストラクターに追加して注釈@JsonCreatorを削除すると、期待どおりに動作するはずです。@JsonProperty

于 2015-09-22T19:01:28.870 に答える
3

Github での私の回答の CP:

次のコードをテストし、テストに合格しました。

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-modules の最新の状態に対してテストされ、すべての依存関係が 2.6.2 に設定されていることに注意してください。

于 2015-09-22T19:43:37.360 に答える