Gson 循环引用的映射

在这篇文章中,我们将讨论一个特别讨厌的话题:循环引用。 我们可能在计算机科学或图表数学课上听说过循环引用。 在更实际的解释中:它处理对象具有指向不同对象的嵌套属性的情况,然后引用回原始对象。 这种情况会导致 Gson 使我们的应用程序崩溃。 如果想知道如何处理此问题,那么继续阅读吧!


Gson 和 StackOverflowError

这篇文章的替代标题可以是:为什么 Gson 会导致 java.lang.StackOverflowError?

只需从序列化的角度考虑:正如我们在嵌套对象文章中所了解到的,Gson 需要为每个引用的对象创建一个新的 JSON 子元素。 如果对象 A 引用对象 B,Gson 将需要在 A 中为 B 创建一个子元素。如果 B 引用回 A,我们将陷入无限循环,并将继续创建新的子元素,从而创建新的子元素,依此类推。 最终,这将触发 StackOverflowError

让我们用一些实际的代码来可视化这个场景:

public class UserCircular {  
    String name;
    String email;
    int age;
    boolean isDeveloper;

    // 对子节点和父节点的引用
    UserCircular child;
    UserCircular parent;
}

想象一下,我们的应用构建了一个家谱。 然后每个家庭成员都可能被包裹在上面你可以看到的 UserCircular 类中。 好吧,child 和 parent 属性可能应该是一个列表,但这不会改变任何东西。

从编程的角度来看,这是一个完全有效的实现。

UserCircular userObject = new UserCircular();

UserCircular child = new UserCircular();

userObject.setName("zhangsan");
userObject.setAge(46);
userObject.setEmail("zadmei_onmpw@163.com");

child.setName("zhangtian");
child.setAge(22);
child.setEmail("no");
child.setParent(userObject);

userObject.setChild(child);

Gson gson = new Gson();
String userJson = gson.toJson(userObject);

不幸的是,我们将无法对其进行序列化。 没有 JSON 等价物具有相同的数据格式,因为我们将陷入无限循环。导致出现 java.lang.StackOverflowError 错误

Gson 循环引用的映射

解决方案很简单:我们必须从序列化中排除一个图形引用。 一种简单的方法是将 transient 添加到 parent 属性:

public class UserCircular {  
    String name;
    String email;
    int age;
    boolean isDeveloper;

    // 对子节点和父节点的引用
    UserCircular child;
    transient UserCircular parent;
}

在内部,我们的应用程序没有任何变化。 尽管如此, parent 将不再被序列化,并且我们已退出该无限循环:

{
  "name": "zhangsan",
  "email": "zadmei_onmpw@163.com",
  "age": 46,
  "isDeveloper": false,
  "child": {
    "name": "zhangtian",
    "email": "no",
    "age": 22,
    "isDeveloper": false
  }
}

所有必要的数据仍将被序列化。 如果需要,服务器将需要再次创建反向引用。

总结

在这篇文章中,我们了解了 Gson 何时会陷入无限循环并抛出 StackOverflowError。 我们可以通过从序列化中排除一个指针来解决此问题。