我尝试使用jackson序列化一些数据,这对大多数情况都很有效,但现在我有一个列表问题。该列表是A类型,它是一个抽象类,可能包含循环依赖项。我无法弄清楚如何用杰克逊序列化这个结构。 identityInformation和typeInformation的组合似乎没有正常工作。 下面是产生我面临的问题的Examplecode。
我正在使用Jackson版本2.8.3。我错过了什么吗?是否有一个很好的解决方案来序列化和反序列化这种列表?
提前感谢您的帮助!
代码:
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
public class JacksonTest {
@JsonTypeInfo(
use = JsonTypeInfo.Id.CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "@class")
@JsonSubTypes({ @JsonSubTypes.Type(value = B.class) })
public static abstract class A {
public A member;
}
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
public static class B extends A {
}
public static void main(String[] args) {
List<A> list = new ArrayList<A>();
ObjectMapper mapper = new ObjectMapper();
B instance1 = new B();
B instance2 = new B();
instance1.member = instance2;
list.add(instance1);
list.add(instance2);
CollectionType listType = mapper.getTypeFactory().constructCollectionType(list.getClass(), A.class);
try{
String serialized = mapper.writerFor(listType).writeValueAsString(list);
System.out.println(serialized);
list = mapper.readValue(serialized, listType);
} catch (Exception e){
e.printStackTrace();
}
}
}
生成的json String:
[{"@class":"JacksonTest$B","@id":1,"member":{"@class":"JacksonTest$B","@id":2,"member":null}},2]
尝试读取字符串时出错:
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (VALUE_NUMBER_INT), expected FIELD_NAME: missing property '@class' that is to contain type id (for class JacksonTest$A)
看起来像jackson期望第二个条目也是一个包含@class字段的json对象,并且不会识别第二个数组元素是对现有对象的引用。
答案 0 :(得分:2)
字段“A.member”的类型是“A”,你错过了@JsonIdentityInfo的“A”类型。因此,Jackson认为list中的对象是一个普通对象(只有id被序列化),由于缺少identityinfo,jackson在反序列化过程中无法应用对象引用查找机制,因此你会看到错误。 / p>
要解决您的问题,您需要添加“@JsonIdentityInfo for type”A“或者更好,您可以将@JsonIdentityInfo声明从”B“类型移动到”A“。
代码:
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.hamcrest.core.IsSame;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
public class JacksonTest {
@JsonTypeInfo(
use = JsonTypeInfo.Id.CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "@class")
@JsonSubTypes({ @JsonSubTypes.Type(value = B.class) })
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
public static abstract class A {
public A member;
}
public static class B extends A {
}
@Test
public void testCirularPolymorphicSerialization() throws Exception {
ObjectMapper mapper = new ObjectMapper();
// set up fixture
List<A> list = new ArrayList<A>();
B instance1 = new B();
B instance2 = new B();
instance1.member = instance2;
list.add(instance1);
list.add(instance2);
CollectionType listType = mapper.getTypeFactory().constructCollectionType(list.getClass(), A.class);
String serialized = mapper.writerFor(listType).writeValueAsString(list);
list = mapper.readValue(serialized, listType);
assertThat(list.size(), is(2));
assertThat(list.get(0).member, sameInstance(list.get(1)));
}
}