Джексон путается с двунаправленным отношением "один ко многим"

Я использую jackson 1.9.2 с Hibernate/ Spring MVC через MappingJacksonHttpMessageConverter.

Джексон не может сериализовать двунаправленное отношение "один ко многим" и делает бесконечный цикл.

Я использую следующие классы:

  • Разговор, содержащий набор экземпляров SMS.

  • Каждый экземпляр SMS имеет набор телефонных номеров

  • Каждый PhoneNumber имеет родительский контакт (это двунаправленное отношение "много к одному" )

Я пытаюсь сделать сериализацию беседы.

Если я не использую @JsonManagedReference и @JsonBackReference, тогда джексон будет разбиваться из-за бесконечного цикла. Но когда я их использую, Контакт не сериализуется в PhoneNumber.

Class Contact {
  @JsonManagedReference
  List<PhoneNumber> phoneNumber ;
}
Class PhoneNumber {
  @JsonBackReference 
  Contact contact;
}

Вывод:

{    <--------------------- Conversation
    "id": 51,
    "smsSet": [
      {
        "id": 53,
        "origin": 0123465,
        "destination": "06533844XY",
        "message": "Hello world!",
        "phoneNumbers": [
          {
            "id": 64,
            "num": "06533844XY",
            "creationDate": 1333992533000,
          }
        ],
      }
    ],
    "creationDate": 1333992534000
  }

вместо

{    <---------- conversation
    "id": 51,
    "smsSet": [
      {
        "id": 53,
        "origin": 0123465,
        "destination": "06533844XY",
        "message": "Hello world!",
        "phoneNumbers": [
          {
            "id": 64,
            "num": "06533844XY",
            "creationDate": 1333992533000,
            "contact":  <--------------------- Missing part
             {
                "id": 12,
                "name": "Samuel Jackson",
                "primaryNumber": "06533844XY"
             }
          }
        ],
      }
    ],
    "creationDate": 1333992534000
  }

Ответ 1

Недавно я столкнулся с аналогичной проблемой: Джексон - сериализация сущностей с двунаправленными отношениями (избегая циклов)

Итак, решение состоит в том, чтобы перейти на 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>

И собственный .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 1. *

Ответ 2

В качестве первого решения я сохранил эти аннотации и создал другой класс: ContactWithouPhoneNumber и добавил его как поле в класс PhoneNumber.

Теперь, перед рендерингом, я скопирую все поля, кроме PhoneNumber, из контакта в контактWithoutPhoneNumber. Выход JSON содержит все, что мне нужно.

Это шаблон проектирования DTO.

Ответ 3

Одна вещь, неверная в вашем классе def, - это использование нетипизированного List; вам лучше:

List<PhoneNumber> phoneNumber ;

так как иначе Джексон не может знать, что такое тип при десериализации; и даже при сериализации это может вызвать проблемы (поскольку базовый тип неизвестен). Поэтому я бы сначала исправить эту проблему.

Но кроме того, ваша зависимость может быть неправильной: @JsonManagedReference ДОЛЖЕН всегда быть первым, что нужно для сериализации. Если это не так, вы не можете использовать эти аннотации. Не видя объектов, которые вы пытаетесь сериализовать, трудно точно знать (определение POJO и JSON слегка отключаются).

Ответ 4

Вы можете добавить @JsonIgnore к полям, и в процессе сериализации картатор пропустит их. Он аналогичен функции @Transient.

Это в jackson-core-asl.