杰克逊前

通过。开始使用Spring 5和Spring Boot 2学习的春天课程:

>>查看课程

1.概述

在本教程中,我们将讨论处理的最佳方法杰克逊的双向关系

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

2.无限递归

首先,让我们看一下Jackson无限递归问题。在下面的例子中,我们有两个实体——”用户”和“”——与一个简单的一对多关系:

用户“实体:

public class User {public int id;公共字符串名称;公共列表> <项userItems;}

“实体:

public class Item {public int id;公共字符串itemName;公共用户所有者;}

当我们试图序列化",杰克逊会抛出一个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(项);新的objectmap () .writeValueAsString(项);}

完整的异常是:

无限递归(StackOverflowError)(通过引用链:org. baeldun.jackson.bidirection。金宝搏188体育项目(“业主”)——> org.bael金宝搏188体育dung.jackson.bidirection。用户(“userItems”)- > java.util。ArrayList [0] - > o金宝搏188体育rg.baeldung.jackson.bidirection。项目(“所有者 "] ->.....

让我们看看,在接下来的几节课程中——如何解决这个问题。

3.使用@JsonManagedReference,@JsonBackReference

首先,让我们注释与的关系@JsonManagedReference,@JsonBackReference为了让杰克逊更好地处理这一关系:

这是“用户“实体:

public class User {public int id;公共字符串名称;@JsonManagedReference public List userItems;}

和“”:

public class Item {public int id;公共字符串itemName;@JsonBackReference公共用户所有者;}

让我们现在测试新的实体:

@Test public void givenbidirectionrelation_whenusingjacksonreferenceannotationwithserialization_thcorrect () throws JsonProcessingException{最终用户用户=新用户(1,“约翰”);final Item Item = new Item(2, "book", user);user.addItem(项);final String itemJson = new objectapper ().writeValueAsString(item);final String userJson = new objectapper ().writeValueAsString(user);为了(itemJson containsString(“书”));为了(itemJson,而不是(containsString(“约翰”)));为了(userJson containsString(“约翰”));为了(userJson containsString (userItems "));为了(userJson containsString(“书”)); }

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

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

下面是序列化User对象的输出:

{" id ": 1,“名字”:“约翰”,“userItems”:[{" id ": 2,“itemName”:“书”}]}

注意:

  • @JsonManagedReference是reference的前向部分——通常被序列化的部分。
  • @JsonBackReference是引用的后面部分——它将在序列化中被省略。
  • 序列化对象不包含对用户对象。

另外,请注意,我们不能在注释之间切换。下面的代码将用于序列化:

@JsonBackReference public List userItems;@JsonManagedReference公共用户所有者;

但在试图反序列化对象时将抛出异常,如@JsonBackReference不能在集合上使用。

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

4.使用@JsonIdentityInfo

现在,让我们看看如何帮助序列化具有双向关系的实体@JsonIdentityInfo

我们将类级注解添加到用户“实体:

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

到““实体:

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

测试时间:

@Test public void givenBidirectionRelation_whenUsingJsonIdentityInfo_thenCorrect() throws JsonProcessingException {User User = new User(1, "John");Item Item = new Item(2, "book", user);user.addItem(项);String result = new objectapper ().writeValueAsString(item);为了(因此,containsString(“书”));为了(因此,containsString(“约翰”));为了(因此,containsString (userItems "));}

下面是序列化的输出:

{" id ": 2,“itemName”:“书”,“主人”:{" id ": 1、“名称”:“约翰”,“userItems”:[2]}}

5.使用@JsonIgnore

或者,我们也可以使用@JsonIgnore注释只是忽略关系中的某一方面,从而打破了这个链条。

在下面的例子中-我们将通过忽略“用户“财产”userItems“从序列化:

这是“用户“实体:

public class User {public int id;公共字符串名称;@JsonIgnore public List userItems;}

下面是我们的测试:

@Test public void givenBidirectionRelation_whenUsingJsonIgnore_thenCorrect() throws JsonProcessingException {User User = new User(1, "John");Item Item = new Item(2, "book", user);user.addItem(项);String result = new objectapper ().writeValueAsString(item);为了(因此,containsString(“书”));为了(因此,containsString(“约翰”));为了(结果,而不是(containsString(“userItems”)));}

下面是序列化的输出:

{" id ": 2,“itemName”:“书”,“主人”:{" id ": 1、“名称”:“约翰”}}

6.使用@JsonView

我们也可以用更新的@JsonView注释以排除关系的一侧。

在下面的例子中-我们使用两个JSON视图-公共内部在哪里内部扩展公共:

public static class(静态类)public static class(内部类)public static class(内部类)

我们将包括所有用户字段公共视图- - - - - -除了用户userItems哪些将包括在内部观点:

这是我们的实体用户”:

@JsonView(Views.Public.class) public int id;@JsonView(Views.Public.class) public String name;@JsonView(Views.Internal.class) public List userItems;}

这就是我们的实体"”:

public class Item {@JsonView(Views.Public.class) public int id;@JsonView(Views.Public.class) public String itemName;@JsonView(Views.Public.class) public User owner;}

序列化时使用公共视图,它正确工作-因为我们排除在外userItems从被序列化:

@Test public void givenbidirectionrelation_whenusingpublicjsonview_thencright () throws JsonProcessingException {User User = new User(1, "John");Item Item = new Item(2, "book", user);user.addItem(项);String result = new objectapper ().writerWithView(Views.Public.class) .writeValueAsString(item);为了(因此,containsString(“书”));为了(因此,containsString(“约翰”));为了(结果,而不是(containsString(“userItems”)));}

但如果我们序列化使用内部看来,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(项);new objectapper () .writerWithView(Views.Internal.class) .writeValueAsString(item);}

7.使用自定义序列化器

接下来——让我们看看如何使用自定义序列化器来序列化具有双向关系的实体。

在以下示例中——我们将使用自定义序列化器来序列化"用户“财产”userItems”:

这是“用户“实体:

public class User {public int id;公共字符串名称;@JsonSerialize(using = CustomListSerializer.class) public List userItems;}

这是"CustomListSerializer”:

public class CustomListSerializer extends StdSerializer>{public CustomListSerializer() {this(null);} public CustomListSerializer(Class t) {super(t);} public void serialize(List items, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {List ids = new ArrayList<>();for (Item Item: items) {id .add(Item .id);} generator.writeObject (ids);}}

现在让我们测试一下序列化器,看看产生了什么样的输出:

@Test public void givenBidirectionRelation_whenUsingCustomSerializer_thenCorrect() throws JsonProcessingException {User User = new User(1, "John");Item Item = new Item(2, "book", user);user.addItem(项);String result = new objectapper ().writeValueAsString(item);为了(因此,containsString(“书”));为了(因此,containsString(“约翰”));为了(因此,containsString (userItems "));}

最终的输出使用自定义序列化器的序列化:

{" id ": 2,“itemName”:“书”,“主人”:{" id ": 1、“名称”:“约翰”,“userItems”:[2]}}

8.反序列化,@JsonIdentityInfo

现在,让我们来看看如何反序列化具有双向关系的实体@JsonIdentityInfo

这是"用户“实体:

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

和““实体:

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

现在让我们写一个快速的测试-从一些我们想要解析和完成正确构造的实体的手动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 objectapper ().readerFor(ItemWithIdentity.class).readValue(json);item.id assertequal (2);assertequal(“书”,item.itemName);assertequal(“约翰”,item.owner.name);}

9.使用自定义序列化器

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

在以下示例中——我们将使用自定义反序列化器来解析“用户“财产”userItems”:

这里的“用户“实体:

public class User {public int id;公共字符串名称;@JsonDeserialize(using = CustomListDeserializer.class) public List userItems;}

这是我们的CustomListDeserializer”:

public class CustomListDeserializer extends StdDeserializer>{public CustomListDeserializer() {this(null);}公共CustomListDeserializer(类< ?vc) {super(vc);} @Override public List deserialize(JsonParser JsonParser, DeserializationContext context) throws IOException, JsonProcessingException {return new ArrayList<>();}}

还有一个简单的测试:

@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 objectapper ().readerFor(Item.class).readValue(json);item.id assertequal (2);assertequal(“书”,item.itemName);assertequal(“约翰”,item.owner.name);}

10.结论

在本教程中,我们演示了如何使用Jackson对具有双向关系的实体进行序列化/反序列化。

所有这些示例和代码片段的实现可以在我们的GitHub项目-这是一个基于maven的项目,所以它应该很容易导入和运行。

杰克逊底部

通过。开始使用Spring 5和Spring Boot 2学习的春天课程:

>>查看课程
44评论
最古老的
最新的
内联反馈
查看所有评论
本文评论关闭!