redochka
redochka

Reputation: 12839

Jackson confused with bidirectional one-to-many relationship

I'm using jackson 1.9.2 with Hibernate/Spring MVC through MappingJacksonHttpMessageConverter.

Jackson can not serialize bidirectional one-to-many relationship and makes an infinite loop.

The classes I'm using are :

What I am trying to do is to serialize a conversation.

If I don't use @JsonManagedReference and @JsonBackReference then jackson will crashe due to an infinite loop. But when I use them, the Contact doesn't get serialized into the PhoneNumber.

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

The output is :

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

instead of

{    <---------- 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
  }

Upvotes: 11

Views: 14050

Answers (4)

redochka
redochka

Reputation: 12839

As a first solution, I kept these annotations and created an other class: ContactWithouPhoneNumber and added it as a field to the PhoneNumber class.

Now, before rendering, I copy all fields except PhoneNumber, from contact to contactWithoutPhoneNumber. The output JSON contains every thing I need.

This is the DTO design pattern.

Upvotes: 2

eric
eric

Reputation: 31

You can add @JsonIgnore to fields and the mapper will skip over them during serialization. It is similar in function to @Transient.

It is in the jackson-core-asl.

Upvotes: 0

Eugene Retunsky
Eugene Retunsky

Reputation: 13139

I recently encountered a similar problem: Jackson - serialization of entities with birectional relationships (avoiding cycles)

So the solution is to upgrade to Jackson 2.0, and add to classes the following annotation:

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

Then the problem is that Spring doesn't work with Jackson 2.0. This has been solved in the following way:

<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>

And the own.implementation.of.MappingJacksonHttpMessageConverter is based on this:

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

But use ObjectMapper and other Jackson classes from Jackson 2.0 instead of Jackson 1.*

Upvotes: 7

StaxMan
StaxMan

Reputation: 116590

One thing wrong in your class def is use of untyped List; you should rather have:

List<PhoneNumber> phoneNumber ;

since otherwise Jackson can not know what the type is when deserializing; and even on serializing it could cause problems (as base type is not known for sure). So I would first fix this issue.

But additionally your dependency may be wrong way around: @JsonManagedReference MUST always be the first thing to serialize. If this is not the case, you can not use these annotations. Without seeing objects you are trying to serialize it is hard to know for sure (POJO definition and JSON seem slightly off).

Upvotes: 0

Related Questions