1. Overview
In this tutorial we’ll go over the best ways to deal with bidirectional relationships in Jackson.
We’ll discuss the Jackson JSON infinite recursion problem, then – we’ll see how to serialize entities with bidirectional relationships and finally – we will deserialize them.
2. Jackson Infinite Recursion
First – let’s take a look at the Jackson infinite recursion problem. In the following example we have two entities – “User” and “Item” – with a simple one-to-many relationship:
The “User” entity:
public class User {
    public int id;
    public String name;
    public List<Item> userItems;
}
The “Item” entity:
public class Item {
    public int id;
    public String itemName;
    public User owner;
}
When we try to serialize an instance of “Item“, Jackson will throw a JsonMappingException exception:
@Test(expected = JsonMappingException.class)
public void givenBidirectionRelation_whenSerializing_thenException()
  throws JsonProcessingException {
    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);
    new ObjectMapper().writeValueAsString(item);
}
The full exception is:
com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: org.baeldung.jackson.bidirection.Item["owner"] ->org.baeldung.jackson.bidirection.User["userItems"] ->java.util.ArrayList[0] ->org.baeldung.jackson.bidirection.Item["owner"] ->…..
Let’s see, over the course of the next few sections – how to solve this problem.
3. Use @JsonManagedReference, @JsonBackReference
First, let’s annotate the relationship with @JsonManagedReference, @JsonBackReference to allow Jackson to better handle the relation:
Here’s the “User” entity:
public class User {
    public int id;
    public String name;
    @JsonBackReference
    public List<Item> userItems;
}
And the “Item“:
public class Item {
    public int id;
    public String itemName;
    @JsonManagedReference
    public User owner;
}
Let’s now test out the new entities:
@Test
public void givenBidirectionRelation_whenUsingJacksonReferenceAnnotation_thenCorrect()
  throws JsonProcessingException {
    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);
    String result = new ObjectMapper().writeValueAsString(item);
    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("userItems")));
}
Here is the output of serialization:
{
 "id":2,
 "itemName":"book",
 "owner":
    {
        "id":1,
        "name":"John"
    }
}
Note that:
- @JsonManagedReference is the forward part of reference – the one that gets serialized normally.
- @JsonBackReference is the back part of reference – it will be omitted from serialization.
4. Use @JsonIdentityInfo
Now – let’s see how to help with the serialization of entities with bidirectional relationship using @JsonIdentityInfo.
We add the class level annotation to our “User” entity:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class User { ... }
And to the “Item” entity:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Item { ... }
Time for the test:
@Test
public void givenBidirectionRelation_whenUsingJsonIdentityInfo_thenCorrect()
  throws JsonProcessingException {
    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);
    String result = new ObjectMapper().writeValueAsString(item);
    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, containsString("userItems"));
}
Here is the output of serialization:
{
 "id":2,
 "itemName":"book",
 "owner":
    {
        "id":1,
        "name":"John",
        "userItems":[2]
    }
}
5. Use @JsonIgnore
Alternatively, we can also use the @JsonIgnore annotation to simply ignore one of the sides of the relationship, thus braking the chain.
In the following example – we will prevent the infinite recursion by ignoring the “User” property “userItems” from serialization:
Here is “User” entity:
public class User {
    public int id;
    public String name;
    @JsonIgnore
    public List<Item> userItems;
}
And here is our test:
@Test
public void givenBidirectionRelation_whenUsingJsonIgnore_thenCorrect()
  throws JsonProcessingException {
    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);
    String result = new ObjectMapper().writeValueAsString(item);
    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("userItems")));
}
And here is the output of serialization:
{
 "id":2,
 "itemName":"book",
 "owner":
    {
        "id":1,
        "name":"John"
    }
}
6. Use a Custom Serializer
Next – let’s see how to serialize entities with bidirectional relationship using a custom serializer.
In the following example – we will use custom serializer to serialize the “User” property “userItems“:
Here’s the “User” entity:
public class User {
    public int id;
    public String name;
    @JsonSerialize(using = CustomListSerializer.class)
    public List<Item> userItems;
}
And here is the “CustomListSerializer“:
public class CustomListSerializer extends JsonSerializer<List<Item>>{
    @Override
    public void serialize(List<Item> items, JsonGenerator generator, SerializerProvider provider) 
      throws IOException, JsonProcessingException {
        List<Integer> ids = new ArrayList<Integer>();
        for (Item item : items) {
            ids.add(item.id);
        }
        generator.writeObject(ids);
    }
}
Let’s now test out the serializer and see the right kind of output being produced:
@Test
public void givenBidirectionRelation_whenUsingCustomSerializer_thenCorrect()
  throws JsonProcessingException {
    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);
    String result = new ObjectMapper().writeValueAsString(item);
    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, containsString("userItems"));
}
And the final output of the serialization with the custom serializer:
{
 "id":2,
 "itemName":"book",
 "owner":
    {
        "id":1,
        "name":"John",
        "userItems":[2]
    }
}
7. Deserialize with @JsonIdentityInfo
Now – let’s see how to deserialize entities with bidirectional relationship using @JsonIdentityInfo.
Here is the “User” entity:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class User { ... }
And the “Item” entity:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Item { ... }
Let’s now write a quick test – starting with some manual JSON data we want to parse and finishing with the correctly constructed entity:
@Test
public void givenBidirectionRelation_whenDeserializingWithIdentity_thenCorrect() 
  throws JsonProcessingException, IOException {
    String json = 
      "{\"id\":2,\"itemName\":\"book\",\"owner\":{\"id\":1,\"name\":\"John\",\"userItems\":[2]}}";
    ItemWithIdentity item = new ObjectMapper().reader(ItemWithIdentity.class).readValue(json);
    assertEquals(2, item.id);
    assertEquals("book", item.itemName);
    assertEquals("John", item.owner.name);
}
8. Use Custom Deserializer
Finally, let’s deserialize the entities with bidirectional relationship using a custom deserializer.
In the following example – we will use custom deserializer to parse the “User” property “userItems“:
Here is “User” entity:
public class User {
    public int id;
    public String name;
    @JsonDeserialize(using = CustomListDeserializer.class)
    public List<Item> userItems;
}
And here is our “CustomListDeserializer“:
public class CustomListDeserializer extends JsonDeserializer<List<Item>>{
    @Override
    public List<Item> deserialize(JsonParser jsonparser, DeserializationContext context) 
      throws IOException, JsonProcessingException {
        return new ArrayList<Item>();
    }
}
And here is our test:
@Test
public void givenBidirectionRelation_whenUsingCustomDeserializer_thenCorrect()
  throws JsonProcessingException, IOException {
    String json = 
      "{\"id\":2,\"itemName\":\"book\",\"owner\":{\"id\":1,\"name\":\"John\",\"userItems\":[2]}}";
    Item item = new ObjectMapper().reader(Item.class).readValue(json);
    assertEquals(2, item.id);
    assertEquals("book", item.itemName);
    assertEquals("John", item.owner.name);
}
9. Conclusion
We learned how serialize/deserialize entities with bidirectional relationship using Jackson.
You can find the source code here.
