How to solve circular link in json serializer caused by bidirectional sleep mode matching?

I am writing a serializer to serialize POJO to JSON, but am stuck in a circular reference problem. In hibernate, the one-to-many bidirectional relationship of parent links accesses the parent and child objects back to the parents, and here my serializer dies. (see sample code below)
How to break this cycle? Can we get the object owner tree to see if the object itself exists in its own owner hierarchy? Any other way to find if the link will be circular? or any other idea to solve this problem?

+64
java json serialization hibernate
Jul 27 '10 at 3:11
source share
11 answers

Is it possible to imagine bidirectional communication in JSON? Some data formats are not suitable for some types of data modeling.

One way to handle loops when working with object bypass graphs is to keep track of which objects you have seen so far (using identity comparisons) to prevent a transition from an infinite loop.

+9
Jul 27 '10 at 3:20
source share

I rely on Google JSON to solve this problem using function

Field exclusion from serialization and deserialization

Suppose the bidirectional relationship between classes A and B is as follows

public class A implements Serializable { private B b; } 

And B

 public class B implements Serializable { private A a; } 

Now use GsonBuilder. To get a custom Gson object as follows (note the setExclusionStrategies method)

 Gson gson = new GsonBuilder() .setExclusionStrategies(new ExclusionStrategy() { public boolean shouldSkipClass(Class<?> clazz) { return (clazz == B.class); } /** * Custom field exclusion goes here */ public boolean shouldSkipField(FieldAttributes f) { return false; } }) /** * Use serializeNulls method if you want To serialize null values * By default, Gson does not serialize null values */ .serializeNulls() .create(); 

Now our circular link

 A a = new A(); B b = new B(); a.setB(b); b.setA(a); String json = gson.toJson(a); System.out.println(json); 

Take a look at the GsonBuilder class

+46
Jul 27 '10 at 7:16
source share

Jackson 1.6 (released in September 2010) has some annotation-based support for handling such a parent / child binding, see http://wiki.fasterxml.com/JacksonFeatureBiDirReferences . ( Snapshot )

You can, of course, already eliminate the serialization of the parent link, which already uses most JSON processing packages (jackson, gson and flex-json at least support it), but the real trick is how to deserialize it back (recreate the parent link) rather than just handle serialization. Although it seems that now just an exception might work for you.

EDIT (April 2012): Jackson 2.0 now supports true identity links ( Snapshot ), so you can also solve this problem.

+33
Jul 29 '10 at 6:12
source share

In solving this problem, I applied the following approach (standardizing the process in my application, making the code clear and multiple):

  • Create an annotation class that will be used in the fields that you would like to exclude.
  • Define a class that implements the Google ExclusionStrategy interface
  • Create a simple method to create a GSON object using GsonBuilder (similar to Arthur's explanation)
  • Annotate fields to be excluded if necessary
  • Apply serialization rules to com.google.gson.Gson
  • Serialize Your Object

Here is the code:

one)

 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface GsonExclude { } 

2)

 import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; public class GsonExclusionStrategy implements ExclusionStrategy{ private final Class<?> typeToExclude; public GsonExclusionStrategy(Class<?> clazz){ this.typeToExclude = clazz; } @Override public boolean shouldSkipClass(Class<?> clazz) { return ( this.typeToExclude != null && this.typeToExclude == clazz ) || clazz.getAnnotation(GsonExclude.class) != null; } @Override public boolean shouldSkipField(FieldAttributes f) { return f.getAnnotation(GsonExclude.class) != null; } } 

3)

 static Gson createGsonFromBuilder( ExclusionStrategy exs ){ GsonBuilder gsonbuilder = new GsonBuilder(); gsonbuilder.setExclusionStrategies(exs); return gsonbuilder.serializeNulls().create(); } 

four)

 public class MyObjectToBeSerialized implements Serializable{ private static final long serialVersionID = 123L; Integer serializeThis; String serializeThisToo; Date optionalSerialize; @GsonExclude @ManyToOne(fetch=FetchType.LAZY, optional=false) @JoinColumn(name="refobj_id", insertable=false, updatable=false, nullable=false) private MyObjectThatGetsCircular dontSerializeMe; ...GETTERS AND SETTERS... } 

5)

In the first case, null is provided to the constructor, you can specify another class that should be excluded - both options are added below

 Gson gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(null) ); Gson _gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(Date.class) ); 

6)

 MyObjectToBeSerialized _myobject = someMethodThatGetsMyObject(); String jsonRepresentation = gsonObj.toJson(_myobject); 

or to exclude a Date object

 String jsonRepresentation = _gsonObj.toJson(_myobject); 
+12
Apr 17 2018-11-11T00:
source share

If you use Jackon for serialization, just apply @JsonBackReference to your bi-directional mapping. It solves the circular reference problem.

Note: @JsonBackReference is used to solve infinite recursion (StackOverflowError)

+3
Jun 06 '17 at 6:34 on
source share

If you use Javascript, there is a very simple solution using the replacer parameter of the JSON.stringify() method, where you can pass a function to change the default serialization behavior.

You can use it here. Consider the example below with 4 nodes in a cyclic graph.

 // node constructor function Node(key, value) { this.name = key; this.value = value; this.next = null; } //create some nodes var n1 = new Node("A", 1); var n2 = new Node("B", 2); var n3 = new Node("C", 3); var n4 = new Node("D", 4); // setup some cyclic references n1.next = n2; n2.next = n3; n3.next = n4; n4.next = n1; function normalStringify(jsonObject) { // this will generate an error when trying to serialize // an object with cyclic references console.log(JSON.stringify(jsonObject)); } function cyclicStringify(jsonObject) { // this will successfully serialize objects with cyclic // references by supplying @name for an object already // serialized instead of passing the actual object again, // thus breaking the vicious circle :) var alreadyVisited = []; var serializedData = JSON.stringify(jsonObject, function(key, value) { if (typeof value == "object") { if (alreadyVisited.indexOf(value.name) >= 0) { // do something other that putting the reference, like // putting some name that you can use to build the // reference again later, for eg. return "@" + value.name; } alreadyVisited.push(value.name); } return value; }); console.log(serializedData); } 

Later, you can easily recreate the actual object with circular references by analyzing the serialized data and changing the next property to point to the actual object if it uses a named link with @ , as in this example.

+1
Mar 31 '14 at 15:23
source share

It uses a solution similar to Arthur, but instead of setExclusionStrategies I used

 Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create(); 

and the @Expose annotation is used for the fields I need in json, other fields are excluded.

+1
Nov 17 '14 at 10:45
source share

This is how I finally solved it in my case. This works, at least with Gsen and Jackson.

 private static final Gson gson = buildGson(); private static Gson buildGson() { return new GsonBuilder().addSerializationExclusionStrategy( getExclusionStrategy() ).create(); } private static ExclusionStrategy getExclusionStrategy() { ExclusionStrategy exlStrategy = new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes fas) { return ( null != fas.getAnnotation(ManyToOne.class) ); } @Override public boolean shouldSkipClass(Class<?> classO) { return ( null != classO.getAnnotation(ManyToOne.class) ); } }; return exlStrategy; } 
+1
Nov 17 '16 at 9:35
source share

This error can be added if you have two objects:

 class object1{ private object2 o2; } class object2{ private object1 o1; } 

Using GSon for serialization, I got this error:

 java.lang.IllegalStateException: circular reference error Offending field: o1 

To solve this problem, just add the transition keyword:

 class object1{ private object2 o2; } class object2{ transient private object1 o1; } 

As you can see here: Why does Java have transition fields?

The Java transient keyword is used to indicate that the field should not be serialized.

0
Jul 19. '16 at 13:19
source share

answer number 8 is better, I think that if you know which field is throwing the error, you only set fild to null and decide.

 List<RequestMessage> requestMessages = lazyLoadPaginated(first, pageSize, sortField, sortOrder, filters, joinWith); for (RequestMessage requestMessage : requestMessages) { Hibernate.initialize(requestMessage.getService()); Hibernate.initialize(requestMessage.getService().getGroupService()); Hibernate.initialize(requestMessage.getRequestMessageProfessionals()); for (RequestMessageProfessional rmp : requestMessage.getRequestMessageProfessionals()) { Hibernate.initialize(rmp.getProfessional()); rmp.setRequestMessage(null); // ** } } 

To make the code readable, a large comment moves from the comment // ** below.

java.lang.StackOverflowError [Error processing request; The nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Failed to write JSON: Infinite recursion (StackOverflowError) (via the reference chain: com.service.pegazo.bo.RequestMessageProfessional ["requestMessage.peaz.gazage.peaz.gazage.peaz.gazage.peaz.azazazpez.azazazazezazazazegazazemazazazegazazemazazazegazazazemazazegazazemazazegazazegorazazhego.az coma β†’ .RequestMessage ["requestMessageProfessionals"]

-3
Nov 20 '15 at 12:01
source share

For example, ProductBean has serialBean. The display will be bidirectional. If we now try to use gson.toJson() , we end up with a circular link. To avoid this problem, you can follow these steps:

  • Get results from a data source.
  • Iterate over the list and make sure serialBean is not null, and then
  • Set productBean.serialBean.productBean = null;
  • Then try using gson.toJson();

This should solve the problem.

-eleven
Feb 10 '11 at 14:45
source share



All Articles