杰克逊的@JsonSubTypes仍然是多态反序列化所必需的吗?

时间:2015-07-28 00:36:40

标签: java json jackson json-deserialization

我能够序列化和反序列化抽象基类使用

注释的类层次结构
@JsonTypeInfo(
    use = JsonTypeInfo.Id.MINIMAL_CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class")

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

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

所以...我是一个尚未记录的功能的受益者,还是我依赖​​于无证件行为(可能会改变)或是其他事情发生了? (我问,因为我真的不想让它成为后两者中的任何一个。但是I gots to know。)

编辑:添加代码 - 和一条评论。评论是:我应该提到我反序列化的所有子类都与基本抽象类在同一个包和同一个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相同,只是在a和{{String中声明int(不是SubB)字段boolean {}}中的{}}(不是int)(方法SubC会相应地修改)。

测试类:

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); } } 行中断,则可以检查try {并查看:

actual1

因此,三个子类被正确序列化(每个子类的类名称为id),然后反序列化,结果比较相等(每个子类都有一个"值类型" {"pb1":{"@class":".SubA","a":5}, "pb2":{"@class":".SubB","a":"foobar"}, "pb3":{"@class":".SubC","a":true}} )。 / p>

1 个答案:

答案 0 :(得分:46)

在Jackson中进行序列化和反序列化有两种方法可以实现多态性。它们在您发布的link中的第1部分用法中定义。

您的代码

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

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

  

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

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

提供
  

表示具有最小路径的Java类名称用作类型标识符。

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

你也可以使用JsonTypeInfo.Id#NAME

  

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

要提供此类逻辑类型名称,请使用@JsonSubTypes

  

JsonTypeInfo一起使用的注释,用于指示子类型   可序列化的多态类型,以及关联的逻辑名称   在JSON内容中(比使用物理Java更便携)   班级名称)。

这只是实现相同结果的另一种方式。您询问有关州的文件

  

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

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