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
Is Hymanson's @JsonSubTypes still necessary for polymorphic 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 @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.
但是没有@JsonSubTypes
列出子类,而且子类本身相对没有注释,@JsonCreator
构造函数上只有一个。ObjectMapper 是普通的,我没有使用 mixin。
Hymanson 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 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 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).
子类SubB
和SubC
是相同的,除了字段a
被声明为String
(not int
) inSubB
和boolean
(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 actual1
and 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#NAME
which
你也可以使用JsonTypeInfo.Id#NAME
which
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
JsonTypeInfo
to 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.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.
因此JsonTypeInfo.Id
,处理类名的各种值是直截了当的,因为它们可以自动生成。但是,对于类型名称,您需要明确给出映射值。