【问题标题】:Jackson confused with bidirectional one-to-many relationship杰克逊对双向一对多关系感到困惑
【发布时间】:2012-04-10 23:22:08
【问题描述】:

我通过 MappingJacksonHttpMessageConverter 使用带有 Hibernate/Spring MVC 的 jackson 1.9.2。

Jackson 无法序列化双向一对多关系,造成无限循环。

我使用的类是:

  • 具有一组 SMS 实例的对话。

  • 每个 SMS 实例都有一组电话号码

  • 每个 PhoneNumber 都有一个父联系人(这是双向多对一关系)

我想做的是序列化对话。

如果我不使用 @JsonManagedReference@JsonBackReference,那么 jackson 将由于无限循环而崩溃。但是当我使用它们时,Contact 不会被序列化为 PhoneNumber

班级联系{ @JsonManagedReference List phoneNumber ; } 类电话号码 { @JsonBackReference 联系方式; }

输出是:

{

而不是

{

【问题讨论】:

    标签: json jackson


    【解决方案1】:

    我最近遇到了一个类似的问题:Jackson - 具有双向关系的实体序列化(避免循环)

    所以解决方案是升级到 Jackson 2.0,并在类中添加以下注释:

    @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, 
                      property = "@id")
    public class SomeEntityClass ...
    

    那么问题是 Spring 不适用于 Jackson 2.0。这已通过以下方式解决:

    <bean id="jacksonMessageConverter"
              class="own.implementation.of.MappingJacksonHttpMessageConverter"/>
    
    <bean class="org.springframework.web.servlet.mvc
                 .annotation.AnnotationMethodHandlerAdapter">
            <property name="messageConverters">
                <list>
                    <ref bean="jacksonMessageConverter"/>
                </list>
            </property>
            <property name="requireSession" value="false"/>
        </bean>
    

    而own.implementation.of.MappingJacksonHttpMessageConverter就是基于这个:

    http://www.jarvana.com/jarvana/view/org/springframework/spring-web/3.0.0.RELEASE/spring-web-3.0.0.RELEASE-sources.jar!/org/springframework/http/converter/json/MappingJacksonHttpMessageConverter.java?format=ok

    但是使用 ObjectMapper 和 Jackson 2.0 中的其他 Jackson 类而不是 Jackson 1.*

    【讨论】:

    • 谢谢,刚刚试了一下,它似乎只适用于返回的对话列表中的第一个对话。任何的想法?我应该把 JsonIdentityInfo 放在哪里?
    • 您需要为两个类(Contact 和 PhoneNumber)放置 JsonIdentityInfo 并删除 JsonBack/ManagedReference 注释(据我从您的问题中了解到)。
    • 当我删除 JsonBack/ManagedReference 注释时,我收到以下错误:com.fasterxml.jackson.databind.JsonMappingException: Class com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator has no default (无参数)构造函数(通过引用链:java.util.ArrayList[0]->com.model.Conversation["smsSet"]->java.util.LinkedHashSet[0]->com.model.SmsBean["phoneNumbers" ]->org.hibernate.collection.PersistentSet[0])
    • 我和@redochka 有同样的问题。上面的解决方案似乎没问题,但总体上运行良好,但如果在列表中一个对象在列表中出现两次,第二次尝试时只出现对象 id 而不是对象本身
    【解决方案2】:

    作为第一个解决方案,我保留了这些注释并创建了另一个类:ContactWithouPhoneNumber,并将其作为字段添加到 PhoneNumber 类中。

    现在,在渲染之前,我将除 PhoneNumber 之外的所有字段从联系人复制到 contactWithoutPhoneNumber。输出 JSON 包含我需要的所有内容。

    这是 DTO 设计模式。

    【讨论】:

      【解决方案3】:

      你的类定义中的一个错误是使用无类型的List;你应该有:

      List<PhoneNumber> phoneNumber ;
      

      否则 Jackson 在反序列化时无法知道类型是什么;甚至在序列化时也可能导致问题(因为不确定基类型)。 所以我会先解决这个问题。

      但另外你的依赖可能是错误的:@JsonManagedReference 必须始终是序列化的第一件事。如果不是这种情况,则不能使用这些注释。 如果没有看到您尝试序列化的对象,则很难确定(POJO 定义和 JSON 似乎略有不同)。

      【讨论】:

      • 我用的是通用版,*编辑器已经吞下了<.>
      • 嗯,好的。是的,这似乎更有可能;否则错误会有所不同。
      【解决方案4】:

      您可以将@JsonIgnore 添加到字段,映射器将在序列化期间跳过它们。它的功能类似于@Transient。

      它在 jackson-core-asl 中。

      【讨论】:

      • 这并不是真正的解决方案。他需要对这些字段进行序列化。