【问题标题】:Parent-child relation between two objects causes JSON StackOverflowError两个对象之间的父子关系导致 JSON StackOverflowError
【发布时间】:2020-06-01 14:06:46
【问题描述】:

我正在尝试在某些对象之间实现父子关系,但遇到了一些麻烦。

就我而言,我试图将对象存储在其他对象中(例如,容器存储多个项目或其他包含项目的容器)。棘手的部分是存储中的每个对象都应该能够分辨出它的最外层父对象是什么。虽然这似乎在我的内存数据库中工作(目前使用 h2),但尝试获取我所有存储项目的 JSON 表示会给出这个(我返回 List<StorageUnit>):

无法编写 JSON:无限递归 (StackOverflowError);嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (通过引用链: java.util.ArrayList[0]->com.warehousing.storage.FixedContentsCase["contents"]->java.util .ArrayList[0]->com.warehousing.storage.FixedContentsCase["contents"]->...

以下是课程:

存储单元

@Entity
@Inheritance
public abstract class StorageUnit {

   @Id
   @GeneratedValue(strategy=GenerationType.IDENTITY)
   private Long id;
   @ManyToOne
   private Location location;
   protected Long parentContainerId;
   // <getters & setters> 
   public abstract List<StorageUnit> getContents();
}

FixedContentCase


@Entity
public class FixedContentsCase extends StorageUnit {
    @OneToMany
    private List<Item> items; 

    public FixedContentsCase() {
        super();
        items = new ArrayList<>();
    }
    // <getters & setters> 
    @Override
    public List<StorageUnit> getContents() {
        // Return the case itself and its contents
        List<StorageUnit> contents = new ArrayList<>(Arrays.asList(this));
        for (StorageUnit item : items) 
            contents.addAll(item.getContents());
        return contents;
    }   
}

物品

@Entity
public class Item extends StorageUnit {

    private String description;

    public Item() {
        super();
        this.description = "";
    }
    // <getters & setters> 
    @Override
    public List<StorageUnit> getContents() {
        return Arrays.asList(this);
    }   
}

我尝试用@JsonIgnoreProperties("parentContainerId") 注释StorageUnit 类,但没有成功。用@JsonIgnore 注释parentContainerId 也没有帮助。我还尝试注释 getter 而不是属性本身(根据following)。有没有办法解决这个问题,或者是否需要进行某种设计更改?谢谢!

【问题讨论】:

  • 是的,我不确定这是否不可能,但您需要做或至少我会做的是使用 DTO 模式,这意味着您创建的类具有所有这些属性,例如“parentName”, “childName”、“grandChildName”等等,如您所见,它们将具有它们的 getter 设置器,这就是您将在 json 中发送的内容。随时要求澄清任何事情
  • 如果你删除以下Arrays.asList(this)会发生什么?
  • @InsertKnowledge 我认为这也可能是问题所在,但它并没有改变任何东西
  • @BugsForBreakfast 我以前没有听说过 DTO 方法,这似乎是正确的方法。但是,我选择不对给定容器中可以存储多少子容器设置限制。如果我需要定义像“grandgrandChildName”这样的属性,这将使它有点麻烦。谢谢!
  • @bekauz 是的,这是正确的方法,考虑到未来的类似任务,在这种情况下,案例的命名和广泛性确实有点难以处理,在这个如果您应该尝试根据业务流程定义 json 将拥有的最大子对象是什么,就像他们应该告诉您“不,这东西通常不会有超过 3 个子对象”,所以这样你就可以让它拥有最多5个孩子。我知道我的英语在这里不是很好,但我想你明白我的意思,哈哈,不管怎样,看看它对很多情况都非常有用的模式 c:

标签: java json spring orm jackson


【解决方案1】:

使用 Jackson,这绝对可以通过 @JsonIgnore 之类的注释或提到的 DTO 方法 BugsForBreakfast 来实现。

我创建了一个杰克逊 MixIn 处理程序来允许动态过滤,我用它来避免 DTO 的样板

https://github.com/Antibrumm/jackson-antpathfilter

自述文件中的示例应该说明它是如何工作的,以及它是否适合您。

【讨论】:

    【解决方案2】:

    您的问题是您将存储单元本身添加到其内容列表中,如果您向下遍历树,则会导致无限递归。解决方案:使用引用并且只序列化对象一次,使用@JsonIdentityInfo@JsonIdentityReference

    public class MyTest {
    
        @Test
        public void myTest() throws JsonProcessingException {
            final FixedContentsCase fcc = new FixedContentsCase();
            fcc.setId(Long.valueOf(1));
            final Item item = new Item();
            item.setId(Long.valueOf(2));
            item.setDescription("item 1");
            fcc.getItems().add(item);
            final ObjectMapper om = new ObjectMapper();
            System.out.println(om.writeValueAsString(fcc));
        }
    }
    
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonIdentityReference(alwaysAsId = false)
    class Item extends StorageUnit {
    
        ...
    }
    
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonIdentityReference(alwaysAsId = false)
    class FixedContentsCase extends StorageUnit {
    
        ...
    }
    
    abstract class StorageUnit {
    
        ...
    }
    

    【讨论】:

    • 确实是问题所在,使用@JsonIdentityInfo@JsonIdentityReference 效果很好。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多