Java Jackson反序列化不';如果同时使用多态类型和构建器,则无法工作

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

给定的测试失败了,但我认为不应该

当数据对象转换为常规的非生成器类时,测试通过(源:)

要通过builder对象的测试,必须在
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{
列出动物=李