【问题标题】:Java Jackson 2.8.3 serializing list containing abstract objects with circular dependenciesJava Jackson 2.8.3 序列化列表包含具有循环依赖关系的抽象对象
【发布时间】:2016-10-24 18:29:36
【问题描述】:

我尝试使用 jackson 序列化一些数据,这在大多数情况下效果很好,但现在我遇到了列表问题。该列表属于 A 类型,它是一个抽象类,可能包含循环依赖项。我不知道如何用杰克逊序列化这个结构。 identityInformation 和 typeInformation 的组合似乎无法正常工作。 下面是产生我面临的问题的示例代码。

我使用的是 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字符串:

[{"@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)

似乎杰克逊希望第二个条目也是一个包含 @class 字段的 json 对象,并且不承认第二个数组元素是对现有对象的引用。

【问题讨论】:

    标签: java jackson


    【解决方案1】:

    字段“A.member”的类型是“A”,您错过了“A”类型的 @JsonIdentityInfo。因此,Jackson 认为列表中的对象是普通对象(只有 id 被序列化),并且由于缺少身份信息,jackson 在反序列化期间无法应用对象引用查找机制,因此您会看到错误。

    要解决您的问题,您还需要为“A”类型添加“@JsonIdentityInfo”,或者您可以将@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)));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-22
      • 2020-05-20
      • 1970-01-01
      • 2012-03-12
      • 1970-01-01
      相关资源
      最近更新 更多