@JsonTypeInfo(
use = JsonTypeInfo.Id.MINIMAL_CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "@class")
但是没有列出子类的@JsonSubTypes
,而且子类本身也没有太多的注释,只有构造函数上的@JsonCreator
。 ObjectMapper是普通的,我没有使用mixin。
Jackson文档关于多态反序列化和“类型标识符”建议(强烈)在抽象基类上使用@JsonSubTypes
注解,或者在mixin上使用它,或者需要向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();
}
}
子类
SubB
和SubC
是相同的,除了在SubB
中字段a
声明为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);
}
}
这个测试通过了。如果你在第二个
try {
行出现错误,你可以检查actual1
,看一下结果:{"pb1":{"@class":".SubA","a":5},
"pb2":{"@class":".SubB","a":"foobar"},
"pb3":{"@class":".SubC","a":true}}
所以这三个子类被正确地序列化(每个子类都有其类名作为id),然后反序列化,结果相等(每个子类都有一个“值类型”的
equals()
)。
ObjectMapper#registerSubtypes
,可能需要结合Reflections等工具。 - chrylis -cautiouslyoptimistic-@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, property="message_type")
,然后将每个类都注册到新创建的映射器中:mapper.registerSubtypes(new NamedType(SomeClass.class, "some_class"));
。当您在运行时知道所有类但无法更改基类时,这种方法是可行的,因此您不需要在JsonSubTypes中列出所有内容。 - Wheezil