杰克逊的@JsonSubTypes仍然是多态反序列化的必要条件吗? [英] Is Jackson's @JsonSubTypes still necessary for polymorphic deserialization?

查看:138
本文介绍了杰克逊的@JsonSubTypes仍然是多态反序列化的必要条件吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我能够序列化和反序列化一个类层次结构,其中抽象基类用

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")

但没有 @JsonSubTypes 列出子类,子类本身相对未注释,只有 @JsonCreator 在构造函数上。 ObjectMapper是vanilla,我没有使用mixin。

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

关于 PolymorphicDeserialization和type ids建议(强烈地)我需要抽象基类上的 @JsonSubTypes 注释,或者在mixin上使用它,或者我需要使用ObjectMapper注册子类型。并且有很多SO问题和/或博客帖子都同意。但它确实有效。 (这是Jackson 2.6.0。

Jackson documentation on PolymorphicDeserialization and "type ids" suggests (strongly) I need the @JsonSubTypes annotation 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 Jackson 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.)

编辑:添加代码 - 和一条评论。注释是:我应该提到我反序列化的所有子类都在与基本抽象类相同的包和相同的jar中。

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.

抽象基类:

package so;
import com.fasterxml.jackson.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);
}

它的子类:

package so;
import org.apache.commons.lang3.builder.EqualsBuilder;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.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();
    }
}

子类 SubB SubC 是相同的,除了字段 a 被声明为 String (不是 int SubB boolean (不是 int )在 SubC 中(方法 getA 相应修改。)

Subclasses SubB and SubC are the same except that field a is declared String (not int) in SubB and boolean (not int) in SubC (and the method getA is modified accordingly).

测试类:

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.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.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 jackson_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);
    }
}

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

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

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

因此三个子类被正确序列化(每个子类的类名称为id),然后反序列化,结果比较相等(每个子类都有一个值类型 equals())。

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()).

推荐答案

有两种方法可以实现多态性与Jackson的序列化和反序列化。它们在链接第1部分用法中定义。 >你发布了。

There are two ways to achieve polymorphism in serialization and deserialization with Jackson. They are defined in Section 1. Usage in the link you posted.

您的代码

@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)

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

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具有最小路径的类名用作类型标识符。

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

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

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.

您也可以使用 JsonTypeInfo.Id #NAME 哪个

You could have also used JsonTypeInfo.Id#NAME which


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

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

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

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


使用的注释使用 JsonTypeInfo 来指示
可序列化多态类型的子类型,以及关联逻辑名在JSON内容中使用
(其中比使用物理Java
类名更容易移植。

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

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

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


基于Java类名的类型ID是直接的
:它是只是类名,可能是一些简单的前缀
删除(对于最小变体)。但是类型名称是不同的:一个有
来在逻辑名和实际类之间进行映射。

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.

所以各种 JsonTypeInfo.Id 处理类名的值很简单,因为它们可以自动生成。但是,对于类型名称,您需要明确地给出映射值。

So the various JsonTypeInfo.Id values 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.

这篇关于杰克逊的@JsonSubTypes仍然是多态反序列化的必要条件吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆