Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson JSON serialization - specify recursion level

I'm using SpringMVC 3.2.4 and am looking to use Jackson2 to serialize an object to a JSON output.

The object has a recursive property. If I try to serialize it using a default Jackson ObjectMapper, I get a recursion error. I realize that I can use @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class) to prevent the recursion, however, I actually want the recursion to make it easier to parse in a mustache template. However, I would like to limit the recursion level.

Is there anyway to specify to the Jackson serializer to recurse only 1 level? If I need to create my own serializer, can I register it such that it is only used for a specific object class?

As indicate by a couple of comments, there is already another question on SO that very closely relates to this one: Jackson JSON serialization, recursion avoidance by level defining. However, in that question, the accepted answer (as well as the others), all indicate how to avoid recursion by Jackson by using @JsonIdentityInfo. In this particular case, I am not looking to limit it; rather I want it. However, I just want to limit the depth of the recursion.

Additionally, the referenced SO question provides links to some Jackson documentation; I have already the docs, but the Jackson docs, quite frankly, are quite lacking. They indicate how to register a serializer, but do not indicate how it needs to be structured. Nor, are there any indications as to how to determine the recursive level of the serializer. Finally, there is no indication if/how one can register a serializer for Jackson in Spring to apply to specific types of classes.

like image 434
Eric B. Avatar asked Dec 23 '13 04:12

Eric B.


1 Answers

If anyone comes across this issue it is possible to limit the recursion depth. @JsonIdentityInfo, @JsonBackReference, @JsonManagedReference offer an ok solution only for some cases.

One issue I had with @JsonIdentityInfo is that sometimes first level children would not get serialised due to recursion depth on another child, example:

@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...
}

What would this would yield in my instance is:

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

serialiser would descent into child A a; and C c1 but not C c2 to me it's more important to have first level children serialised.

Another issue was that serialisation depth was not always the same on all children.

Solution: Ignore relations during object serialisation and add them in your serialiser method depending on depth.

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;
}

This will descend into each child for depth levels the last object will be without relations and thus the recursion cut-off point.

Now I can do: (new A()).toJson(3) this will serialise object A with B relation descend into object B serialise it it will go back into A but wont descend back into B again. It will than descend into C and back into B reaching the final cut off point.

Works pretty well and you can adjust the depth to your needs.

like image 64
Sterling Duchess Avatar answered Nov 14 '22 09:11

Sterling Duchess