Jackson – Bidirectional Relationships – Jackson – 双向关系

最后修改: 2015年 1月 1日

1. Overview

1.概述

In this tutorial, we’ll examine the best ways to deal with bidirectional relationships in Jackson.

在本教程中,我们将研究在Jackson中处理双向关系的最佳方法

First, we’ll discuss the Jackson JSON infinite recursion problem. Then we’ll see how to serialize entities with bidirectional relationships. Finally, we’ll deserialize them.

首先,我们将讨论Jackson JSON的无限递归问题。然后我们将看到如何序列化具有双向关系的实体。最后,我们将对它们进行反序列化。

2. Infinite Recursion

2.无限的递归

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:

让我们来看看Jackson的无限递归问题。在下面的例子中,我们有两个实体,”User“和”Item,“,有一个简单的一对多关系

The “User” entity:

用户“实体。

public class User {
    public int id;
    public String name;
    public List<Item> userItems;
}

The “Item” entity:

Item“实体。

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:

当我们试图序列化一个”Item,“的实例时,Jackson将抛出一个JsonMappingException异常。

@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"]
->…..

We’ll see over the course of the next few sections how to solve this problem.

我们将在接下来的几节中看到如何解决这个问题。

3. Use @JsonManagedReference, @JsonBackReference

3.使用@JsonManagedReference, @JsonBackReference

First, let’s annotate the relationship with @JsonManagedReference, and @JsonBackReference to allow Jackson to better handle the relation:

首先,让我们用@JsonManagedReference@JsonBackReference来注释这个关系,以使Jackson能够更好地处理这个关系。

Here’s the “User” entity:

这里是”User“实体。

public class User {
    public int id;
    public String name;

    @JsonManagedReference
    public List<Item> userItems;
}

And the “Item“:

还有”Item“。

public class Item {
    public int id;
    public String itemName;

    @JsonBackReference
    public User owner;
}

Now let’s test out the new entities:

现在我们来测试一下新的实体。

@Test
public void givenBidirectionRelation_whenUsingJacksonReferenceAnnotationWithSerialization_thenCorrect() throws JsonProcessingException {
    final User user = new User(1, "John");
    final Item item = new Item(2, "book", user);
    user.addItem(item);

    final String itemJson = new ObjectMapper().writeValueAsString(item);
    final String userJson = new ObjectMapper().writeValueAsString(user);

    assertThat(itemJson, containsString("book"));
    assertThat(itemJson, not(containsString("John")));

    assertThat(userJson, containsString("John"));
    assertThat(userJson, containsString("userItems"));
    assertThat(userJson, containsString("book"));
}

Here’s the output of serializing the Item object:

下面是序列化Item对象的输出。

{
 "id":2,
 "itemName":"book"
}

And here’s the output of serializing the User object:

这里是序列化User对象的输出。

{
 "id":1,
 "name":"John",
 "userItems":[{
   "id":2,
   "itemName":"book"}]
}

Note that:

请注意,。

  • @JsonManagedReference is the forward part of reference, the one that gets serialized normally.
  • @JsonBackReference is the back part of reference; it’ll be omitted from serialization.
  • The serialized Item object doesn’t contain a reference to the User object.

Also note that we can’t switch around the annotations. The following will work for the serialization:

还要注意的是,我们不能随意切换注释。下面的方法将对序列化起作用。

@JsonBackReference
public List<Item> userItems;

@JsonManagedReference
public User owner;

But when we attempt to deserialize the object, it’ll throw an exception, as @JsonBackReference can’t be used on a collection.

但是当我们试图反序列化这个对象时,它会抛出一个异常,因为@JsonBackReference不能用于一个集合。

If we want to have the serialized Item object contain a reference to the User, we need to use @JsonIdentityInfo. We’ll look at this in the next section.

如果我们想让序列化的 Item 对象包含对 User 的引用,我们需要使用 @JsonIdentityInfo。我们将在下一节中讨论这个问题。

4. Use @JsonIdentityInfo

4.使用@JsonIdentityInfo

Now let’s learn how we can help with the serialization of entities with bidirectional relationships using @JsonIdentityInfo.

现在让我们来学习如何使用@JsonIdentityInfo来帮助具有双向关系的实体进行序列化。

We’ll add the class level annotation to our “User” entity:

我们将为我们的”User“实体添加类级注解。

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id")
public class User { ... }

And to the “Item” entity:

而对”Item“实体。

@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’s the output of serialization:

下面是序列化的输出。

{
 "id":2,
 "itemName":"book",
 "owner":
    {
        "id":1,
        "name":"John",
        "userItems":[2]
    }
}

5. Use @JsonIgnore

5.使用@JsonIgnore

Alternatively, we can use the @JsonIgnore annotation to simply ignore one of the sides of the relationship, thus breaking the chain.

另外,我们可以使用@JsonIgnore注解来简单地忽略关系中的一方,从而断开链条。

In the following example, we’ll prevent the infinite recursion by ignoring the “User” property “userItems” from serialization:

在下面的例子中,我们将通过忽略序列化中的”User“属性”userItems“来防止无限递归。

Here’s the “User” entity:

这里是”User“实体。

public class User {
    public int id;
    public String name;

    @JsonIgnore
    public List<Item> userItems;
}

And here’s 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")));
}

Finally, here’s the output of serialization:

最后,这里是序列化的输出。

{
 "id":2,
 "itemName":"book",
 "owner":
    {
        "id":1,
        "name":"John"
    }
}

6. Use @JsonView

6.使用@JsonView

We can also use the newer @JsonView annotation to exclude one side of the relationship.

我们还可以使用较新的@JsonView注解来排除关系中的一方。

In the following example, we’ll use two JSON Views, Public and Internal, where Internal extends Public:

在下面的例子中,我们将使用两个JSON视图,PublicInternal,其中Internal扩展了Public

public class Views {
    public static class Public {}

    public static class Internal extends Public {}
}

We’ll include all User and Item fields in the Public View except the User field userItems, which will be included in the Internal View:

我们将在Public View中包含所有UserItem字段 除了User字段userItems,之外,它将被包含在Internal View中。

Here’s our “User” entity:

这里是我们的“用户”实体

public class User {
    @JsonView(Views.Public.class)
    public int id;

    @JsonView(Views.Public.class)
    public String name;

    @JsonView(Views.Internal.class)
    public List<Item> userItems;
}

And here’s our “Item” entity:

这里是我们的”Item”实体。

public class Item {
    @JsonView(Views.Public.class)
    public int id;

    @JsonView(Views.Public.class)
    public String itemName;

    @JsonView(Views.Public.class)
    public User owner;
}

When we serialize using the Public view, it works correctly because we excluded userItems from being serialized:

当我们使用Public视图进行序列化时,工作正常,因为我们将userItems排除在序列化之外。

@Test
public void givenBidirectionRelation_whenUsingPublicJsonView_thenCorrect() 
  throws JsonProcessingException {
 
    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);

    String result = new ObjectMapper().writerWithView(Views.Public.class)
      .writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("userItems")));
}

But if we serialize using an Internal view, JsonMappingException is thrown because all the fields are included:

但是如果我们使用Internal视图进行序列化,JsonMappingException就会被抛出,因为所有的字段都包括在内。

@Test(expected = JsonMappingException.class)
public void givenBidirectionRelation_whenUsingInternalJsonView_thenException()
  throws JsonProcessingException {
 
    User user = new User(1, "John");
    Item item = new Item(2, "book", user);
    user.addItem(item);

    new ObjectMapper()
      .writerWithView(Views.Internal.class)
      .writeValueAsString(item);
}

7. Use a Custom Serializer

7.使用自定义的序列化器

Next, we’ll see how to serialize entities with bidirectional relationships using a custom serializer.

接下来,我们将看到如何使用一个自定义的序列化器来序列化具有双向关系的实体。

In the following example, we’ll use a custom serializer to serialize the “User” property “userItems:

在下面的例子中,我们将使用一个自定义的序列化器来序列化”User“属性”userItems:

Here’s the “User” entity:

这里是”User“实体。

public class User {
    public int id;
    public String name;

    @JsonSerialize(using = CustomListSerializer.class)
    public List<Item> userItems;
}

And here’s the “CustomListSerializer:

这里是”CustomListSerializer:

public class CustomListSerializer extends StdSerializer<List<Item>>{

   public CustomListSerializer() {
        this(null);
    }

    public CustomListSerializer(Class<List> t) {
        super(t);
    }

    @Override
    public void serialize(
      List<Item> items, 
      JsonGenerator generator, 
      SerializerProvider provider) 
      throws IOException, JsonProcessingException {
        
        List<Integer> ids = new ArrayList<>();
        for (Item item : items) {
            ids.add(item.id);
        }
        generator.writeObject(ids);
    }
}

Now let’s test out the serializer. As we can see, the right kind of output is 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"));
}

Here’s the final output of the serialization with the custom serializer:

这是用自定义序列化器进行序列化的最终输出

{
 "id":2,
 "itemName":"book",
 "owner":
    {
        "id":1,
        "name":"John",
        "userItems":[2]
    }
}

8. Deserialize With @JsonIdentityInfo

8.用@JsonIdentityInfo进行反序列化

Now let’s see how to deserialize entities with bidirectional relationships using @JsonIdentityInfo.

现在让我们看看如何使用@JsonIdentityInfo对具有双向关系的实体进行反序列化。

Here’s the “User” entity:

这里是”User“实体。

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id")
public class User { ... }

And the “Item” entity:

还有”Item“实体。

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id")
public class Item { ... }

We’ll write a quick test, starting with some manual JSON data we want to parse, and finishing with the correctly constructed entity:

我们将写一个快速测试,从一些我们想要解析的手动JSON数据开始,最后以正确构建的实体结束。

@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().readerFor(ItemWithIdentity.class).readValue(json);
    
    assertEquals(2, item.id);
    assertEquals("book", item.itemName);
    assertEquals("John", item.owner.name);
}

9. Use Custom Deserializer

9.使用自定义反序列化器

Finally, let’s deserialize the entities with a bidirectional relationship using a custom deserializer.

最后,让我们使用一个自定义的反序列化器对具有双向关系的实体进行反序列化。

In the following example, we’ll use a custom deserializer to parse the “User” property “userItems:

在下面的例子中,我们将使用一个自定义反序列化器来解析”User“属性”userItems:

Here’s the “User” entity:

这里是”User“实体。

public class User {
    public int id;
    public String name;

    @JsonDeserialize(using = CustomListDeserializer.class)
    public List<Item> userItems;
}

And here’s our “CustomListDeserializer:

这里是我们的”CustomListDeserializer:“。

public class CustomListDeserializer extends StdDeserializer<List<Item>>{

    public CustomListDeserializer() {
        this(null);
    }

    public CustomListDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public List<Item> deserialize(
      JsonParser jsonparser, 
      DeserializationContext context) 
      throws IOException, JsonProcessingException {
        
        return new ArrayList<>();
    }
}

Finally, here’s the simple 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().readerFor(Item.class).readValue(json);
 
    assertEquals(2, item.id);
    assertEquals("book", item.itemName);
    assertEquals("John", item.owner.name);
}

10. Conclusion

10.结论

In this article, we illustrated how to serialize/deserialize entities with bidirectional relationships using Jackson.

在这篇文章中,我们说明了如何使用Jackson对具有双向关系的实体进行序列化/反序列化。

The implementation of all of these examples and code snippets can be found in our GitHub project. This is a Maven-based project, so it should be easy to import and run as it is.

所有这些例子和代码片段的实现可以在我们的GitHub项目中找到。这是一个基于Maven的项目,所以应该很容易导入并按原样运行。