【问题标题】:Jackson JSON serialization - specify recursion levelJackson JSON 序列化 - 指定递归级别
【发布时间】:2014-01-11 06:44:09
【问题描述】:

我正在使用 SpringMVC 3.2.4,并希望使用 Jackson2 将对象序列化为 JSON 输出。

对象具有递归属性。如果我尝试使用默认的 Jackson ObjectMapper 对其进行序列化,则会出现递归错误。我意识到我可以使用@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class) 来防止递归,但是,我实际上希望递归更容易在胡子模板中解析。但是,我想限制递归级别。

是否有指定杰克逊序列化程序只递归 1 级?如果我需要创建自己的序列化程序,是否可以将其注册为仅用于特定对象类?

正如几位 cmets 所指出的,SO 上已经有另一个与这个问题密切相关的问题:Jackson JSON serialization, recursion avoidance by level defining。但是,在那个问题中,接受的答案(以及其他答案)都表明如何使用@JsonIdentityInfo 避免杰克逊的递归。在这种特殊情况下,我不想限制它。而是我想要它。但是,我只是想限制递归的深度。

此外,引用的 SO 问题提供了一些 Jackson 文档的链接;我已经有了文档,但坦率地说,杰克逊的文档还很缺乏。它们指示如何注册序列化程序,但不指示它需要如何构建。也没有任何迹象表明如何确定串行器的递归级别。最后,没有迹象表明是否/如何可以在 Spring 中为 Jackson 注册序列化程序以应用于特定类型的类。

【问题讨论】:

  • 这是谷歌上“杰克逊 json 递归限制”的第一个命中,我可能会添加...
  • 同意;我已经读过那个帖子了。我应该在我的问题中指出很多;将编辑以反映这一点。然而,这个问题并不相同。答案是如何避免递归。就我而言,我特别希望有递归;我只是想限制递归行为的深度。接受的响应(以及其他响应)都表明如何防止 Jackson 无限递归。

标签: json spring spring-mvc serialization jackson


【解决方案1】:

如果有人遇到这个问题,可以限制递归深度。 @JsonIdentityInfo, @JsonBackReference, @JsonManagedReference 仅在某些情况下提供好的解决方案。

我对@9​​87654322@ 的一个问题是,有时一级子级由于递归深度对另一个子级的影响不会被序列化,例如:

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id")
class A {
  private long id;
  private B last;

  // Getters, setters...
}

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id")
class B {
  private long id;
  private A a;
  private C c1;
  private C c2;

  // Getters, setters...
}

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id")
class C {
  private long id;
  private Set<A> as;
  private B last;

  // Getters, setters...
}

在我的例子中这会产生什么:

{
  "id": 1,
  "b": {
    "id: 2,
    "a": { ... },
    "c1": { ... },
    "c2": 4
  }
}

序列化器会下降到子 A a;C c1 但不是 C c2 对我来说,让第一级子序列化更重要。

另一个问题是序列化深度在所有子节点上并不总是相同的。

解决方案: 在对象序列化期间忽略关系,并根据深度将它们添加到您的序列化器方法中。

class A {
  private long id;

  @JsonIgnore
  private B last;

  public ObjectNode toJson(int depth) {
    depth = depth - 1;
    ObjectNode node = (new ObjectMapper()).convertValue(this, ObjectNode.class);

    if (depth > 0) {
      // Descend into relations
      ObjectNode b = b.toJson(depth);
      node.set("b", b);
    } 

    return node;
}

这将下降到每个子对象的深度级别,最后一个对象将没有关系,因此是递归截止点。

现在我可以做:(new A()).toJson(3) 这将序列化对象AB 关系下降到对象B 序列化它会返回到A 但不会再次下降到B。然后它将下降到 C 并返回到 B 到达最后的截止点。

效果很好,您可以根据需要调整深度。

【讨论】:

    【解决方案2】:

    您可以使用保持最大深度的线程本地整数来限制序列化深度。在递归 getter 中,根据限制检查当前节点的深度,如果达到限制则返回 null。仅当您可以从节点的内部状态计算当前节点深度时,这才有效。

    请参阅此answer 中的示例代码。

    【讨论】:

      猜你喜欢
      • 2012-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-05
      • 2014-08-02
      • 1970-01-01
      • 2011-04-12
      • 2013-01-18
      相关资源
      最近更新 更多