当前位置: 首页 > 知识库问答 >
问题:

Java Jackson 2.8.3序列化包含具有循环依赖的抽象对象的列表

阚砚文
2023-03-14

我试图用jackson序列化一些数据,这在大多数情况下都很好,但现在我对列表有问题。该列表的类型为 A,它是一个抽象类,可能包含循环依赖项。我不知道如何使用 jackson 序列化这个结构。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)

Jackson 似乎希望第二个条目也是一个包含 @class 字段的 json 对象,并且没有意识到第二个数组元素是对现有对象的引用。

共有1个答案

单于骁
2023-03-14

字段“A.member”的类型是“A”,并且您错过了“A”类型的@JsonIdentityInfo。因此,Jackson 认为列表中的对象是普通对象(只有 id 被序列化),并且由于缺乏 identityinfo,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)));
    }
}
 类似资料:
  • 问题内容: 我有一个对象(解析树),其中包含子节点,这些子节点是对其他节点的引用。 我想使用序列化此对象,但是我得到了 TypeError:循环对象值 因为我提到的结构。 我该如何解决?对我而言,在序列化对象中是否表示对其他节点的引用并不重要。 另一方面,在创建对象时从对象中删除这些属性似乎很乏味,我也不想对解析器(水仙)进行更改。 问题答案: 使用的第二个参数,该替代品的功能,以排除已序列化对象

  • 然后是班级选拔: 教材: 因此,我主要创建新用户并将其序列化: 一切都很完美,但如果我这样做: 然后我想序列化对象用户,程序崩溃了。我得到的结果是: JAVA木卫一。NotSerializableException:在java上。木卫一。ObjectOutputStream。java上的WriteObject 0(ObjectOutputStream.java:1183)。木卫一。ObjectOu

  • 问题内容: 顾名思义,我正在尝试将包含一些BufferedImages(包括其他变量,字符串等)的对象保存到文件中。 我发现了这一点: 如何序列化包含BufferedImages的对象 它的工作原理就像一种魅力,但有一点点挫折:如果您的对象仅包含一个图像,它就可以很好地工作。 我一直在努力获取他的解决方案以处理多个图像(理论上应该可以使用),但是每次读入文件时,我都会得到对象,图像数量正确,但只有

  • 问题内容: 我正在尝试用Java创建一个简单的图像编辑程序。我制作了一个对象,其中包含有关正在编辑的图像的所有信息(一些基本属性,所应用的效果列表,层列表等),我想要一种简单的方法将其保存到磁盘,以便稍后再次打开。 我发现使用Java的defualt 接口可能正是我想要的,我可以将整个对象写到文件中,稍后再读回内存中。但是,包括和不能序列化(其他所有方法都可以)。 我知道可以重写和方法,但我从未这

  • 问题内容: 我有一个使用二进制序列化来保留数据的旧版应用程序。现在,我们想使用Json.net 4.5在不对现有类进行太多更改的情况下序列化数据。 事情一直很好,直到我们遇到了循环依赖类。有解决此问题的解决方法吗? 示例代码如下所示 和测试代码 Edit1 :该问题已报告给Json(http://json.codeplex.com/workitem/23668) Edit2 :序列化在版本4.5

  • 其中备注是对象内部的vavr列表字段。 现在,如果尝试将相同的值反序列化到对象中,则会出现错误: 当尝试反序列化时,为什么serified JSON文本不起作用? 在Java代码中有没有其他方法可以将vavr对象打印成文本格式并将其转换回POJO?