Java Jackson反序列化不';如果同时使用多态类型和构建器,则无法工作
给定的测试失败了,但我认为不应该 当数据对象转换为常规的非生成器类时,测试通过(源:) 要通过builder对象的测试,必须在Java Jackson反序列化不';如果同时使用多态类型和构建器,则无法工作,java,jackson,lombok,jackson-databind,Java,Jackson,Lombok,Jackson Databind,给定的测试失败了,但我认为不应该 当数据对象转换为常规的非生成器类时,测试通过(源:) 要通过builder对象的测试,必须在Animal界面上添加@JsonTypeInfo注释。这意味着Zoo不能是完全通用的,但需要为所有动物提供一个通用的超类型 似乎这种差异不应该存在 杰克逊版本:2.10 错误: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `Jacks
Animal
界面上添加@JsonTypeInfo
注释。这意味着Zoo
不能是完全通用的,但需要为所有动物提供一个通用的超类型
似乎这种差异不应该存在
杰克逊版本:2.10
错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `JacksonTest$Animal` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"{"animals":[{"@type":"Dog","name":"doggo"},{"@type":"Cat","name":"cat"}]}"; line: 1, column: 13] (through reference chain: JacksonTest$Zoo$Builder["animals"]->java.util.ArrayList[0])
完整测试用例:
public class JacksonTest {
@Test
void test() throws JsonProcessingException {
ObjectMapper m = new ObjectMapper();
m.findAndRegisterModules();
List<Animal> animals = List.of(
Dog.builder().name("doggo").build(),
Cat.builder().name("cat").build()
);
Zoo z = Zoo.builder().animals(animals).build();
String json = m.writeValueAsString(z);
Zoo deser = m.readValue(json, Zoo.class);
assertThat(z).isEqualTo(deser);
}
@Value
@Builder(builderClassName = "Builder")
@JsonDeserialize(builder = Zoo.Builder.class)
static class Zoo {
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
List<Animal> animals;
@JsonPOJOBuilder(withPrefix = "")
public static class Builder {}
}
interface Animal {
String getName();
}
@Value
@Builder(builderClassName = "Builder")
@JsonDeserialize(builder = Dog.Builder.class)
static class Dog implements Animal {
String name;
@JsonPOJOBuilder(withPrefix = "")
public static class Builder {}
}
@Value
@Builder(builderClassName = "Builder")
@JsonDeserialize(builder = Cat.Builder.class)
static class Cat implements Animal {
String name;
@JsonPOJOBuilder(withPrefix = "")
public static class Builder {}
}
}
公共类JacksonTest{
@试验
void test()抛出JsonProcessingException{
ObjectMapper m=新的ObjectMapper();
m、 FindRegisterModules();
列出动物(
Dog.builder().name(“doggo”).build(),
Cat.builder().name(“Cat”).build()
);
Zoo z=Zoo.builder().animals(animals.build();
字符串json=m.writeValueAsString(z);
Zoo-desr=m.readValue(json,Zoo.class);
资产(z)isEqualTo(应得);
}
@价值观
@建筑商(builderClassName=“建筑商”)
@JsonDeserialize(builder=Zoo.builder.class)
静态类动物园{
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
列出动物;
@JsonPOJOBuilder(前缀为“”)
公共静态类生成器{}
}
界面动物{
字符串getName();
}
@价值观
@建筑商(builderClassName=“建筑商”)
@JsonDeserialize(builder=Dog.builder.class)
静态类狗实现动物{
字符串名;
@JsonPOJOBuilder(前缀为“”)
公共静态类生成器{}
}
@价值观
@建筑商(builderClassName=“建筑商”)
@JsonDeserialize(builder=Cat.builder.class)
静态类Cat实现动物{
字符串名;
@JsonPOJOBuilder(前缀为“”)
公共静态类生成器{}
}
}
由于我们希望避免添加到动物
接口,我们可以通过添加一个接口来解决这个问题,ZooBuilder
,然后我们将@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
注释添加到动物
mutator方法中,并让Zoo.Builder
实现ZooBuilder
版本:
- 采用OpenJDK 14
- 月食:2020-03(4.15.0)
- junit:5.6.2
- log4j2:2.13.3
- 杰克逊:2.11.0
- 龙目岛:1.18.12
包io.jeffmaxwell.stackoverflow;
导入静态org.junit.jupiter.api.Assertions.assertEquals;
导入java.util.List;
导入org.junit.jupiter.api.BeforeAll;
导入org.junit.jupiter.api.Test;
导入com.fasterxml.jackson.annotation.JsonTypeInfo;
导入com.fasterxml.jackson.core.JsonProcessingException;
导入com.fasterxml.jackson.databind.ObjectMapper;
导入com.fasterxml.jackson.databind.annotation.JsonDeserialize;
导入com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
进口龙目造船厂;
进口龙目山价值;
导入lombok.extern.log4j.Log4j2;
@Log4j2
公共类Q62193465{
静态最终ObjectMapper对象_MAPPER=新ObjectMapper();
@以前
静态无效FindAndRegisterModules(){
OBJECT_MAPPER.findAndRegisterModules();
}
@试验
void test()抛出JsonProcessingException{
列出动物=列出(Dog.builder()的列表)
.name(“doggo”)
.build(),
Cat.builder()
.名称(“猫”)
.build());
var zoo=zoo.builder()
.动物(动物)
.build();
var zooAsJsonString=OBJECT_MAPPER.writeValueAsString(zoo);
info(“zooAsJsonString{}”,zooAsJsonString);
var zooFromJsonString=OBJECT\u MAPPER.readValue(zooAsJsonString,Zoo.class);
assertEquals(动物园、动物园);
}
//增加
界面缩放生成器{
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
动物园。建筑商动物(最终列表动物);
}
@价值观
@建筑商(builderClassName=“建筑商”)
@JsonDeserialize(builder=Zoo.builder.class)
公共静态类动物园{
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
列出动物;
@JsonPOJOBuilder(前缀为“”)
公共静态类生成器实现ZooBuilder{
}
}
界面动物{
字符串getName();
}
@价值观
@建筑商(builderClassName=“建筑商”)
@JsonDeserialize(builder=Dog.builder.class)
静态类狗实现动物{
字符串名;
@JsonPOJOBuilder(前缀为“”)
公共静态类生成器{
}
}
@价值观
@建筑商(builderClassName=“建筑商”)
@JsonDeserialize(builder=Cat.builder.class)
静态类Cat实现动物{
字符串名;
@JsonPOJOBuilder(前缀为“”)
公共静态类生成器{
}
}
}
替代解决方案
这避免了接口,但风险更大,因为它与Lombok生成的更多代码交互
包io.jeffmaxwell.stackoverflow;
导入静态org.junit.jupiter.api.Assertions.assertEquals;
导入java.util.List;
导入org.junit.jupiter.api.BeforeAll;
导入org.junit.jupiter.api.Test;
导入com.fasterxml.jackson.annotation.JsonTypeInfo;
导入com.fasterxml.jackson.core.JsonProcessingException;
导入com.fasterxml.jackson.databind.ObjectMapper;
导入com.fasterxml.jackson.databind.annotation.JsonDeserialize;
导入com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
进口龙目造船厂;
进口龙目山价值;
导入lombok.extern.log4j.Log4j2;
@Log4j2
公共类Q62193465{
静态最终ObjectMapper对象_MAPPER=新ObjectMapper();
@以前
静态无效FindAndRegisterModules(){
OBJECT_MAPPER.findAndRegisterModules();
}
@试验
void test()抛出JsonProcessingException{
列出动物=李