当同时使用多态类型和构造器时,杰克逊反序列化不起作用

问题描述 投票:0回答:1

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

当数据对象转换为常规的非生成器类时,测试通过(源:https://pastebin.com/pBkTb6HW)。

要使构建器对象通过测试,必须在@JsonTypeInfo接口上添加Animal批注。这意味着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 {}
    }
}
java jackson lombok jackson-databind
1个回答
0
投票

已修复,错误代码已注释掉并替换。添加评论。

版本:

  • 采用OpenJDK 14
  • Eclipse:2020-03(4.15.0)
  • junit:5.6.2
  • log4j2:2.13.3
  • 杰克逊:2.11.0
  • lombok:1.18.12

package io.jeffmaxwell.stackoverflow;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;

import lombok.Builder;
import lombok.Value;
import lombok.extern.log4j.Log4j2;

@Log4j2
public class Q62193465 {

    static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @BeforeAll
    static void findAndRegistrerModules() {
        OBJECT_MAPPER.findAndRegisterModules();
    }

    @Test
    void test() throws JsonProcessingException {

        List<Animal> animals = List.of(Dog.builder()
                .name("doggo")
                .build(),
                Cat.builder()
                .name("cat")
                .build());

        var zoo = Zoo.builder()
                .animals(animals)
                .build();

        var zooAsJsonString = OBJECT_MAPPER.writeValueAsString(zoo);
        LOGGER.info("zooAsJsonString {}", zooAsJsonString);
        var zooFromJsonString = OBJECT_MAPPER.readValue(zooAsJsonString, Zoo.class);

        assertEquals(zoo, zooFromJsonString);
    }

    @Value
    @Builder(builderClassName = "Builder")
    @JsonDeserialize(builder = Zoo.Builder.class)
    public static class Zoo {
        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
        List<Animal> animals;

        @JsonPOJOBuilder(withPrefix = "")
        public static class Builder {
        }
    }

    // Added
    @JsonTypeInfo(use = Id.CLASS)
    interface Animal {
        String getName();
    }

    @Value
    @Builder(builderClassName = "Builder")
    // Was @JsonDeserialize(builder = Zoo.Builder.class)
    @JsonDeserialize(builder = Dog.Builder.class)
    static class Dog implements Animal {

        String name;

        @JsonPOJOBuilder(withPrefix = "")
        public static class Builder {
        }
    }

    @Value
    @Builder(builderClassName = "Builder")
    // Was @JsonDeserialize(builder = Zoo.Builder.class)
    @JsonDeserialize(builder = Cat.Builder.class)
    static class Cat implements Animal {

        String name;

        @JsonPOJOBuilder(withPrefix = "")
        public static class Builder {
        }
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.