当前位置: 首页 > 知识库问答 >
问题:

Jackson JDK8数据类型和参数名称模块不能一起使用

章誉
2023-03-14

在我看来,Jackson JDK8数据类型模块偶尔会忽略参数名称模块,这似乎有点令人惊讶,因为两者都需要JDK8并解决与JDK8相关的特定用例。

这里的问题是,如果没有显式指定参数名称(这就是parameter names模块应该做的),我无法找到一种方法使JSON反序列化工作。仅当试图传递JDK8特定类型(<code>可选

问题是-如何使其工作,以便我可以利用参数名称模块(即不需要在构造函数中指定注释值并让它通过参数名称计算出属性名称)?

我可能搞错了,没有看引擎盖下的代码,所以我想听听是否有什么我错过的。

让我们考虑这个简单的例子。

版本栈(撰写本文时的所有最新版本):

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

使用这样的容器运行< code>testSer()会产生:

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

使用如下输入运行< code>testDeser()

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

也可以工作,并产生预期的结果(s1有值,s2Optional.emptymap是空的),但前提是容器构造函数如上定义。我无法让它在以下组合中工作:
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)

我在这里错过了什么?

共有2个答案

齐俊达
2023-03-14

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

请注意,这是针对杰克逊参数名称模块中的最新状态进行测试的,所有依赖项都设置为 2.6.2。

罗业
2023-03-14

我认为这是由于Jackson 2.6的一个剩余问题,即多参数构造函数的检测:尽管检测到参数名称,但如果不使用@JsonCreator注释来标记,构造函数本身不会作为候选保留。这是一个希望在2.7解决的问题(原本应该在2.6解决),但目前是必要的。

如果您将@JsonCreator添加到构造函数中,并删除@JsonProperty注释,事情应该会按预期进行。

 类似资料:
  • 我刚刚更新到Dart2和Flatter sdk:' 无法将参数类型“Object”指定给参数类型“ImageProvider”。), 我只是从弗利特开始,不知道该去哪里找别的。

  • 由于最近的更新,我正面临这个问题,请伙计们帮帮我

  • 我试图用flutter插件HTTP发出HTTP POST请求,但我得到了一个标题错误。有人知道这是什么原因吗?因为在我的其他应用程序中,这工作得非常好?

  • 问题内容: ./chains.go:26:10:不能在作业中使用UpperCaseHandler(typefunc(asl.MessageDelivery))作为asl.MessageHandler类型./chains.go:37:86:无法使用RepeatHandler(类型func(asl.MessageDelivery))与Repeater.ConsumeFunc的参数中的asl.Messa

  • 现在还有另一种可能的使用方法,我不太明白 这个函数可以很容易地接受任何可转换为a的类型。这很奇怪,因为第一个模板参数(“from”)基本上被删除,并在函数调用时推导出来。下面的函数也可以工作,我很肯定它实际上与前面的函数等效 同样,的类型是在调用时推导出来的。

  • 问题内容: 我花了一些时间想知道是否有可能编写一个guice模块,该模块本身使用类型T进行参数化,并使用其type参数指定绑定。 像在此示例(不起作用)中那样: 我尝试了不同的方法,试图将T作为类/ TypeLiteral的实例传递给MyModule,但没有一个起作用。帮助表示赞赏。 问候,zukasz Osipiuk 问题答案: 为此,您将必须使用从头开始构建每个TypeLiteral 。您可以