Jackson JDK8 数据类型和参数名称模块不能一起玩
Jackson JDK8 Data Type and Parameter Names modules don't play together
在我看来,Jackson JDK8 数据类型模块偶尔会忽略参数名称模块,这似乎有点令人惊讶,因为两者都需要 JDK8 并解决与 JDK8 有关的特定用例。
这里的问题是,如果没有明确指定参数名称,我找不到使 JSON 反序列化工作的方法(这就是参数名称模块的全部内容)。它也仅在尝试在容器对象构造函数中传递特定于 JDK8 的类型(Optional<T>
)时才会表现出这种行为(即通常情况下,这有效并且我已经测试过)。代码是用javac参数-parameters
.
编译的
问题是 - 如何让它工作以便我可以利用参数名称模块(即不需要在构造函数中指定注释+值并让它通过以下方式找出 属性 名称参数名称)?
我可能记错了,我没有看引擎盖下的代码,所以我想听听我是否遗漏了什么。
让我们考虑这个简单的例子。
版本堆栈(撰写本文时的所有最新版本):
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" : { }
}
也有效,并产生预期结果(s1
有值,s2
是 Optional.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)
我在这里错过了什么?
我认为这是由于 Jackson 2.6 的一个遗留问题,关于检测多参数构造函数:虽然检测到参数名称,但如果不使用 @JsonCreator
注释,构造函数本身不会保留为候选者标记它。
这是2.7希望解决的问题(本来应该是2.6解决的),暂时还是有必要的
如果您将 @JsonCreator
添加到构造函数并删除 @JsonProperty
注释,事情应该会按预期进行。
我在 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。
在我看来,Jackson JDK8 数据类型模块偶尔会忽略参数名称模块,这似乎有点令人惊讶,因为两者都需要 JDK8 并解决与 JDK8 有关的特定用例。
这里的问题是,如果没有明确指定参数名称,我找不到使 JSON 反序列化工作的方法(这就是参数名称模块的全部内容)。它也仅在尝试在容器对象构造函数中传递特定于 JDK8 的类型(Optional<T>
)时才会表现出这种行为(即通常情况下,这有效并且我已经测试过)。代码是用javac参数-parameters
.
问题是 - 如何让它工作以便我可以利用参数名称模块(即不需要在构造函数中指定注释+值并让它通过以下方式找出 属性 名称参数名称)?
我可能记错了,我没有看引擎盖下的代码,所以我想听听我是否遗漏了什么。
让我们考虑这个简单的例子。
版本堆栈(撰写本文时的所有最新版本):
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" : { }
}
也有效,并产生预期结果(s1
有值,s2
是 Optional.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)
我在这里错过了什么?
我认为这是由于 Jackson 2.6 的一个遗留问题,关于检测多参数构造函数:虽然检测到参数名称,但如果不使用 @JsonCreator
注释,构造函数本身不会保留为候选者标记它。
这是2.7希望解决的问题(本来应该是2.6解决的),暂时还是有必要的
如果您将 @JsonCreator
添加到构造函数并删除 @JsonProperty
注释,事情应该会按预期进行。
我在 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。