How to Solve Circular Reference in JSON Serializer Caused by Hibernate Bidirectional Mapping

How to solve circular reference in json serializer caused by hibernate bidirectional mapping?

Can a bi-directional relationship even be represented in JSON? Some data formats are not good fits for some types of data modelling.

One method for dealing with cycles when dealing with traversing object graphs is to keep track of which objects you've seen so far (using identity comparisons), to prevent yourself from traversing down an infinite cycle.

Why bi-directional ManyToOne causes circular dependency in Hibernate?

You have two solutions :

  1. Use @JsonIgnore on the @ManyToOne
  2. Do NOT serialize your entities. Use DTOs instead and take care while mapping to avoid circular dependencies

Hibernate and JSON - is there a definitive solution to circular dependencies?


Jackson

As said, I was able to solve the problem using

@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=MyEntity.class)` 

for each entity as suggested here.
The scope attribute was necessary to make sure that the name "id" is unique within the scope. Actually, without the scope attribute, as you can see here, it throws an exception saying

com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id java.lang.String) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey@3372bb3f] (through reference chain: ParentEntity["children"]->java.util.ArrayList[0]->ChildEntity["id"])
...stacktrace...
Caused by: java.lang.IllegalStateException: Already had POJO for id (java.lang.String) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey@3372bb3f]
...stacktrace...

Gson

I still haven't found a clean way to serialize circular dependencies.

Json and Java - Circular Reference

There are two ways you can go about this. If you must expose your entity to the outside world, I recommend adding @JsonIgnore on the property that is causing the circular reference. This will tell Jackson not to serialize that property.

Another way is to use the bidirectional features provided by Jackson. You can either use @JsonManagedReference or @JsonBackReference. @JsonManagedReference is the "forward" part of the property and it will get serialized normally. @JsonBackReference is the "back" part of the reference; it will not be serialized, but will be reconstructed when the "forward" type is deserialized.

You can check out the examples here.

This addresses your comment: I think what you might want to do in this case is use a DTO that is visible to the outside world. I like this approach because I don't want to expose my entities to the outside. This means that the Jackson annotations would be on the DTO and not on the enity. You would need some sort of mapper or converter that converts the entity to the DTO. Now when you make changes to your entity, they won't get propagated to the DTO unless you modify your mapper/converter. I think this is ok, because when you make a change to your entity you can decide if you want that change to be exposed or not.

UPDATE

There is a good blog post here that goes into detail about the various ways you can handle bidirectional relationships in Jackson. It describes solutions that use @JsonIgnore, @JsonManagedReference and @JsonBackReference, @JsonIdentityInfo, @JsonView and a custom serializer as well. It's a pretty comprehensive writeup of the various techniques that you can use.

Is there any json library for android which can manage circular reference?

Android will run whatever Java library you choose.

AFAIK, Gson doesn't handle circular references, but Jackson and XStream do.

Check out these threads for further info:

How to solve circular reference in json serializer caused by hibernate bidirectional mapping?

Jackson Vs. Gson

UPD But you're probably better off adding ignore annotations to those back references.

Fix circular reference in symfony when using SerializerInterface

You totally can. Just add this in your framework config.

framework:
serializer:
circular_reference_handler: App\Serializer\MyCustomCircularReferenceHandler

This handler will work globally. Make sure you register it as a service. I does not need to implement any interface. So just a class with an __invoke() will suffice. That invoke will receive the object that is being "circle referenced" as the only argument.

You can either return the id or do some really cool stuff, like creating a uri for the resource. But the implementation details are totally up to you, as long as you don't return the same object, everything will be fine.

:)

Conceptual question on serializing data with circular references

Keep a list of objects you've already serialized. Every object you attempt to serialize should be checked against this list. A similar approach is to toggle a boolean within each object as to whether it's been serialized already, and thus not to continue for this object.



Related Topics



Leave a reply



Submit