当前位置: 首页 > 知识库问答 >
问题:

Jackson的@JsonSubTypes对于多态反序列化仍然是必要的吗?

袁炳
2023-03-14

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

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

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

Jackson留档在PolymoricDeseriize和type id上建议(强烈)我需要抽象基类上的@JsonSubTypes注释,或者在混音上使用它,或者我需要将子类型注册到ObjectMapper。并且有很多SO问题和/或博客文章都同意。然而它是有效的。(这是Jackson 2.6.0。)

所以我是一个尚未被记录的特性的受益者,还是依赖于未被记录的行为(可能会改变),还是有其他事情在发生?(我这么问是因为我真的不想成为后两者中的任何一个。但我必须知道。)

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

抽象基类:

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();
    }
}

子类SubBSubC是相同的,只是字段aSubB中声明了String(不是int)和SubC中的boolean(不是int)(并且相应地修改了方法getA)。

测试等级:

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);
    }
}

此测试通过,如果您在第二行尝试{时中断,您可以检查实际1,并查看:

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

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


共有1个答案

贺方伟
2023-03-14
匿名用户

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

您的代码

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

是第二种方法的示例。首先要注意的是

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

所以这个配置值传播到所有子类型。然后,我们需要一个类型标识符,将Java类型映射到JSON字符串中的文本值,反之亦然。在您的示例中,这由JsonTypeInfo给出。Id#最小_类

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

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

您也可以使用JsonTypeInfo。Id#名称

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

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

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

这只是达到同样结果的另一种方法。你问的关于州的留档

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

因此,各种类型的JsonTypeInfo。处理类名的Id值是直接的,因为它们可以自动生成。然而,对于类型名,需要显式地给出映射值。

 类似资料:
  • 问题内容: 我能够序列化和反序列化一个类层次结构,其中抽象基类用 但是没有列出子类,并且子类本身是相对未注释的,仅在构造函数上具有a 。ObjectMapper是香草的,我没有使用mixin。 Jackson关于PolymorphicDeserialization和“ type id”的 文档建议(强烈)我需要在抽象基类上使用批注,或者在mixin上使用它,或者需要在ObjectMapper中注册

  • 我对Jackson和类型层次结构有以下问题。我正在序列化一个类SubA,该类将扩展为一个字符串,然后尝试将其反序列化回来。当然,在编译时,系统不知道它是基还是SubA,因此我希望它是基,如果它是SubA,则会在编译后执行一些其他操作。 我的基本类看起来像: ...和一个派生自的类: ... 我试图执行以下代码: String是: 但我一次又一次地犯同样的错误。映射器现在知道如何处理另一个类参数-它

  • 我正在使用JacksonPolymorphicDeserialization,这是我的代码,它反序列化到基于“type”属性的适当类中: 它工作得很好,我的json根据“type”值变成了预期的类。 但是,我正在考虑将“type”属性从String移动到Enum,这是我的新代码,带有以下更改: 和枚举: 问题是,第二种方法不起作用。。。知道为什么吗???我可以在这里使用Enum吗??? 谢谢!

  • 我有以下几个类,我想使用Jackson反序列化JSON字符串。 PushNotificationMessage.java 设备信息。Java语言 IOSDeviceInfo。Java语言 WebDeviceInfo.java 我有以下要反序列化的JSON内容: 我只需使用ObjectMapper来尝试执行反序列化。 当我这样做时,我得到: com.fasterxml.jackson.databin

  • 我看过jackson反序列化@JsonTypeInfo的一个例子,那就是: 我试过了,效果很好。现在的问题是,在示例类中,Cat和Dog是从Animal中引用的,我想避免这种情况。有没有一种方法可以将类型绑定从类动物中移除,并且仍然进行反序列化工作?谢谢

  • 我想为我们的REST API实现一个自定义的反序列化器,它不仅被Java应用程序使用。因此,我不想让Jackson将类型信息放入序列化的JSON中。 我目前正在努力反序列化<code>CollectionExpand</code>,因为它包含特定<code>ResourceModel</code>的<code>数据</code>列表。 < code>ResourceModel是一个接口,每个< c