Java Jackson 的 @JsonSubTypes 是否仍然需要多态反序列化?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31665620/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 11:23:19  来源:igfitidea点击:

Is Hymanson's @JsonSubTypes still necessary for polymorphic deserialization?

javajsonHymansonjson-deserialization

提问by davidbak

I am able to serialize and deserialize a class hierarchy where the abstract base class is annotated with

我能够序列化和反序列化抽象基类注释的类层次结构

@JsonTypeInfo(
    use = JsonTypeInfo.Id.MINIMAL_CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class")

but no @JsonSubTypeslisting the subclasses, and the subclasses themselves are relatively unannotated, having only a @JsonCreatoron the constructor. The ObjectMapper is vanilla, and I'm not using a mixin.

但是没有@JsonSubTypes列出子类,而且子类本身相对没有注释,@JsonCreator构造函数上只有一个。ObjectMapper 是普通的,我没有使用 mixin。

Hymanson documentation on PolymorphicDeserialization and "type ids"suggests (strongly) I need the @JsonSubTypesannotation on the abstract base class, or use it on a mixin, or that I need to register the subtypes with the ObjectMapper. And there are plenty of SO questions and/or blog posts that agree. Yet it works. (This is Hymanson 2.6.0.)

关于PolymorphicDeserialization 和“type ids”的Hymanson 文档建议(强烈)我需要@JsonSubTypes抽象基类上的注释,或者在混合中使用它,或者我需要用 ObjectMapper 注册子类型。并且有很多 SO 问题和/或博客文章都同意。然而它有效。(这是Hyman逊 2.6.0。

So ... am I the beneficiary of an as-yet-undocumented feature or am I relying on undocumented behavior (that may change) or is something else going on? (I'm asking because I really don't want it to be either of the latter two. But I gots to know.)

所以......我是一个尚未记录的功能的受益者还是我依赖​​于未记录的行为(可能会改变)或者其他事情正在发生?(我问是因为我真的不希望它是后两者中的任何一个。但我必须知道。)

EDIT: Adding code - and one comment. The comment is: I should have mentioned that all the subclasses I'm deserializing are in the same package and same jar as the base abstract class.

编辑:添加代码 - 和一条评论。评论是:我应该提到我正在反序列化的所有子类与基础抽象类位于同一个包和同一个 jar 中。

Abstract base class:

抽象基类:

package so;
import com.fasterxml.Hymanson.annotation.JsonTypeInfo;

@JsonTypeInfo(
    use = JsonTypeInfo.Id.MINIMAL_CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class")
public abstract class PolyBase
{
    public PolyBase() { }

    @Override
    public abstract boolean equals(Object obj);
}

A subclass of it:

它的一个子类:

package so;
import org.apache.commons.lang3.builder.EqualsBuilder;
import com.fasterxml.Hymanson.annotation.JsonCreator;
import com.fasterxml.Hymanson.annotation.JsonProperty;

public final class SubA extends PolyBase
{
    private final int a;

    @JsonCreator
    public SubA(@JsonProperty("a") int a) { this.a = a; }

    public int getA() { return a; }

    @Override
    public boolean equals(Object obj) {
        if (null == obj) return false;
        if (this == obj) return true;
        if (this.getClass() != obj.getClass()) return false;

        SubA rhs = (SubA) obj;
        return new EqualsBuilder().append(this.a, rhs.a).isEquals();
    }
}

Subclasses SubBand SubCare the same except that field ais declared String(not int) in SubBand boolean(not int) in SubC(and the method getAis modified accordingly).

子类SubBSubC是相同的,除了字段a被声明为String(not int) inSubBboolean(not int) in SubC(并相应地getA修改了方法)。

Test class:

测试类:

package so;    
import java.io.IOException;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.*;
import com.fasterxml.Hymanson.annotation.JsonCreator;
import com.fasterxml.Hymanson.annotation.JsonProperty;
import com.fasterxml.Hymanson.databind.ObjectMapper;

public class TestPoly
{
    public static class TestClass
    {
        public PolyBase pb1, pb2, pb3;

        @JsonCreator
        public TestClass(@JsonProperty("pb1") PolyBase pb1,
                         @JsonProperty("pb2") PolyBase pb2,
                         @JsonProperty("pb3") PolyBase pb3)
        {
            this.pb1 = pb1;
            this.pb2 = pb2;
            this.pb3 = pb3;
        }

        @Override
        public boolean equals(Object obj) {
            if (null == obj) return false;
            if (this == obj) return true;
            if (this.getClass() != obj.getClass()) return false;

            TestClass rhs = (TestClass) obj;
            return new EqualsBuilder().append(pb1, rhs.pb1)
                                      .append(pb2, rhs.pb2)
                                      .append(pb3, rhs.pb3)
                                      .isEquals();
        }
    }

    @Test
    public void Hymanson_should_or_should_not_deserialize_without_JsonSubTypes() {

        // Arrange
        PolyBase pb1 = new SubA(5), pb2 = new SubB("foobar"), pb3 = new SubC(true);
        TestClass sut = new TestClass(pb1, pb2, pb3);

        ObjectMapper mapper = new ObjectMapper();

        // Act
        String actual1 = null;
        TestClass actual2 = null;

        try {
            actual1 = mapper.writeValueAsString(sut);
        } catch (IOException e) {
            fail("didn't serialize", e);
        }

        try {
            actual2 = mapper.readValue(actual1, TestClass.class);
        } catch (IOException e) {
            fail("didn't deserialize", e);
        }

        // Assert
        assertThat(actual2).isEqualTo(sut);
    }
}

This test passes and if you break at the second try {line you can inspect actual1and see:

此测试通过,如果您在第二try {行中断,您可以检查actual1并查看:

{"pb1":{"@class":".SubA","a":5},
 "pb2":{"@class":".SubB","a":"foobar"},
 "pb3":{"@class":".SubC","a":true}}

So the three subclasses got properly serialized (each with their class name as id) and then deserialized, and the result compared equal (each subclass has a "value type" equals()).

所以这三个子类得到了正确的序列化(每个都以它们的类名作为 id)然后反序列化,结果比较相等(每个子类都有一个“值类型” equals())。

采纳答案by Sotirios Delimanolis

There are two ways to achieve polymorphism in serialization and deserialization with Hymanson. They are defined in Section 1. Usagein the linkyou posted.

用Hymanson实现序列化和反序列化的多态有两种方式。它们在第 1 节中定义在您发布的链接中的用法

Your code

你的代码

@JsonTypeInfo(
    use = JsonTypeInfo.Id.MINIMAL_CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class")

is an example of the second approach. The first thing to note is that

是第二种方法的一个例子。首先要注意的是

All instances of annotated type and its subtypes use these settings (unless overridden by another annotation)

注释类型及其子类型的所有实例都使用这些设置(除非被另一个注释覆盖)

So this config value propagates to all subtypes. Then, we need a type identifier that will map a Java type to a text value in the JSON string and vice versa. In your example, this is given by JsonTypeInfo.Id#MINIMAL_CLASS

所以这个配置值传播到所有子类型。然后,我们需要一个类型标识符,它将 Java 类型映射到 JSON 字符串中的文本值,反之亦然。在你的例子中,这是由JsonTypeInfo.Id#MINIMAL_CLASS

Means that Java class name with minimal path is used as the type identifier.

意味着使用最小路径的 Java 类名作为类型标识符。

So a minimal class name is generated from the target instance and written to the JSON content when serializing. Or a minimal class name is used to determine the target type for deserializing.

因此,从目标实例生成一个最小的类名,并在序列化时写入 JSON 内容。或者使用最小的类名来确定反序列化的目标类型。

You could have also used JsonTypeInfo.Id#NAMEwhich

你也可以使用JsonTypeInfo.Id#NAMEwhich

Means that logical type nameis used as type information; name will then need to be separately resolved to actual concrete type (Class).

表示逻辑类型名称用作类型信息;然后需要将名称单独解析为实际的具体类型 ( Class)。

To provide such a logical type name, you use @JsonSubTypes

要提供这样的逻辑类型名称,您可以使用 @JsonSubTypes

Annotation used with JsonTypeInfoto indicate sub types of serializable polymorphic types, and to associate logical namesused within JSON content (which is more portable than using physical Java class names).

用于JsonTypeInfo指示可序列化多态类型的子类型的注释,以及关联JSON 内容中使用的逻辑名称(这比使用物理 Java 类名称更具可移植性)。

This is just another way to achieve the same result. The documentation you're asking about states

这只是实现相同结果的另一种方法。您询问的有关状态的文档

Type ids that are based on Java class name are fairly straight-forward: it's just class name, possibly some simple prefix removal (for "minimal" variant). But type name is different: one has to have mapping between logical name and actual class.

基于 Java 类名的类型 id 相当简单:它只是类名,可能是一些简单的前缀删除(对于“最小”变体)。但是类型名称不同:必须在逻辑名称和实际类之间进行映射。

So the various JsonTypeInfo.Idvalues that deal with class names are straight-forward because they can be auto-generated. For type names, however, you need to give the mapping value explicitly.

因此JsonTypeInfo.Id,处理类名的各种值是直截了当的,因为它们可以自动生成。但是,对于类型名称,您需要明确给出映射值。