Java 是杰克逊';多态反序列化仍然需要s@JsonSubTypes吗?
我能够序列化和反序列化抽象基类注释为Java 是杰克逊';多态反序列化仍然需要s@JsonSubTypes吗?,java,json,jackson,json-deserialization,Java,Json,Jackson,Json Deserialization,我能够序列化和反序列化抽象基类注释为 @JsonTypeInfo( use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") 但是没有列出子类的@JsonSubTypes,子类本身相对没有注释,构造函数上只有一个@JsonCreator。ObjectMapper是香草的,我没有使用mixin 关于的Jackson文档(强烈)建议我需要抽象基类
@JsonTypeInfo(
use = JsonTypeInfo.Id.MINIMAL_CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "@class")
但是没有列出子类的@JsonSubTypes
,子类本身相对没有注释,构造函数上只有一个@JsonCreator
。ObjectMapper是香草的,我没有使用mixin
关于的Jackson文档(强烈)建议我需要抽象基类上的@JsonSubTypes
注释,或者在mixin上使用它,或者我需要这样做。而且有很多这样的问题和/或博客文章都同意这一点。但它确实有效。(这是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();
}
}
子类SubB
和SubC
是相同的,不同的是a
字段在SubB
中声明为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()
)有两种方法可以通过Jackson实现序列化和反序列化中的多态性。它们在您发布的
你的代码
@JsonTypeInfo(
use = JsonTypeInfo.Id.MINIMAL_CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "@class")
是第二种方法的一个例子。首先要注意的是
注释类型及其子类型的所有实例都使用这些设置
(除非被其他注释覆盖)
因此,这个配置值传播到所有子类型。然后,我们需要一个类型标识符,该标识符将Java类型映射到JSON字符串中的文本值,反之亦然
表示使用具有最小路径的Java类名作为类型标识符
因此,从目标实例生成一个最小类名,并在序列化时写入JSON内容。或者使用最小类名确定反序列化的目标类型
你也可以用
表示逻辑类型名称用作类型信息;名称将
然后需要单独解析为实际的具体类型(Class
)
要提供这样的逻辑类型名称,请使用
与JsonTypeInfo
一起使用的注释,用于指示
可序列化多态类型,并使用关联逻辑名称
在JSON内容中(比使用物理Java更具可移植性
类名)
这只是实现相同结果的另一种方法。您所询问的有关状态的文档
基于Java类名的类型ID相当简单
直截了当:它只是类名,可能是一些简单的前缀
删除(对于“最小”变量)。但类型名称不同:一个具有
在逻辑名称和实际类之间进行映射
因此,处理类名的各种
JsonTypeInfo.Id
值都是直接的,因为它们可以自动生成。但是,对于类型名,您需要显式地给出映射值。感谢您解释说,只有在使用名称表单时才需要@JsonSubTypes。所有我读过的文档,我没有想到这些这里最糟糕的是,如果你使用名称形式(这是非常可取的),你就不能动态地添加类或以后添加类。在单个具体类上使用JsonTypeInfo不是更有意义吗?在接口上使用它似乎违反了我想的开闭原则(就像我们需要在每一个新增加的具体实现上修改我们的接口一样)。如果我遗漏了什么,请纠正我here@HDave:您可以使用ObjectMapper#registerSubtypes
,可能与反射类似。正如c-c所提到的,我们成功地使用了@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,property=“message_type”)
然后使用新创建的映射器注册每个类:mapper.registerSubtypes(新名称类型(SomeClass.class,“someu类”);
。当您在运行时知道所有类,但无法更改基类时,此操作有效,因此您不需要在JsonSubTypes中列出所有内容。