I am writing a serializer to serialize POJO to JSON but stuck in circular reference problem. In hibernate bidirectional one-to-many relation, parent references child and child references back to parent and here my serializer dies. (see example code below)
How to break this cycle? Can we get owner tree of an object to see whether object itself exists somewhere in its own owner hierarchy? Any other way to find if the reference is going to be circular? or any other idea to resolve this problem?

- 5,949
- 8
- 49
- 74
-
Did you mean to paste in some code for us to help you resolve your issue? – Russell Jul 27 '10 at 03:14
-
2eugene's annotation based solution is ok, but there is no need for additional annotation and ExclusionStrategy implementation in this case. Just use Java '[transient](http://stackoverflow.com/questions/910374/why-does-java-have-transient-variables)' keyword for that. It works for standard Java object serialization but also [Gson respects it](https://sites.google.com/site/gson/gson-user-guide#TOC-Java-Modifier-Exclusion). – MeTTeO May 20 '12 at 10:04
14 Answers
I rely on Google JSON To handle this kind of issue by using The feature
Suppose a bi-directional relationship between A and B class 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 (Notice 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 reference
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 GsonBuilder class

- 33,349
- 20
- 110
- 136
-
Thank you Arthur for your kind suggestion but the actual question is what is the best way to build so called generic "shouldSkipClass" method. For now I worked on matt's idea and resolved my issue but still skeptic, in future this solution may break in certian scenarios. – WSK Jul 27 '10 at 14:26
-
10This "solves" circular references by removing them. There's no way to rebuild the original data structure from the JSON generated. – Sotirios Delimanolis Feb 17 '15 at 20:45
Jackson 1.6 (released september 2010) has specific annotation-based support for handling such parent/child linkage, see http://wiki.fasterxml.com/JacksonFeatureBiDirReferences. (Wayback Snapshot)
You can of course already exclude serialization of parent link already using most JSON processing packages (jackson, gson and flex-json at least support it), but the real trick is in how to deserialize it back (re-create parent link), not just handle serialization side. Although sounds like for now just exclusion might work for you.
EDIT (April 2012): Jackson 2.0 now supports true identity references (Wayback Snapshot), so you can solve it this way also.
-
how to get this working when you the direction is not always the same.. i tried putting both the annotations on both the fields but it didn't work: Class A{ @JsonBackReference("abc") @JsonManagedReference("xyz") private B b; } Class B{ @JsonManagedReference("abc") @JsonBackReference("xyz") private A a; } – azi Dec 15 '14 at 13:32
-
As per above, Object Id (@JsonIdentityInfo) is the way to make general references work. Managed/Back references do require certain directions, so they will not work for your case. – StaxMan Dec 15 '14 at 17:27
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.

- 138,234
- 66
- 282
- 345
-
I did the same and it works but not sure will it work in all mapping scenarios. At least for now I am well set and will keep thinking on having more elegant idea – WSK Jul 27 '10 at 14:22
-
1Of course they can -- there just isn't native data type or structure for this. But anything can be represented in XML, JSON, or most other data formats. – StaxMan Jul 29 '10 at 06:07
-
4
-
1Multiple ways: keep in mind that generally it's not just JSON, but combination of JSON and some metadata, most commonly class definitions you use to bind to/from JSON. For JSON it's just question of whether to use object identity of some kind, or recreate linkage (Jackson lib for example has a specific way to represent parent/child linkage). – StaxMan Sep 27 '10 at 06:25
In addressing this problem, I took the following approach (standardizing the process across my application, making the code clear and reusable):
- Create an annotation class to be used on fields you'd like excluded
- Define a class which implements Google's ExclusionStrategy interface
- Create a simple method to generate the GSON object using the GsonBuilder (similar to Arthur's explanation)
- Annotate the fields to be excluded as needed
- Apply the serialization rules to your com.google.gson.Gson object
- Serialize your object
Here's the code:
1)
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();
}
4)
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 supplied to the constructor, you can specify another class to 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 the Date object
String jsonRepresentation = _gsonObj.toJson(_myobject);

- 956
- 1
- 11
- 13
-
2this prevents the child object from being processed, can I include the child json and then stop the cyclicity – ir2pid Jun 11 '14 at 12:51
If you are using Jackon to serialize, just apply @JsonBackReference to your bi-directinal mapping It will solve the circular reference problem.
Note : @JsonBackReference is used to solve the Infinite recursion (StackOverflowError)

- 9,486
- 12
- 49
- 67

- 303
- 3
- 13
-
1`@JsonIgnore` made my `JpaRepository` fail to map the property but `@JsonBackReference` solved the circular reference and still allowed proper mapping for the problematic attribute – Bramastic Aug 15 '17 at 23:54
Used a solution similar to Arthur's but instead of setExclusionStrategies
I used
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
and used @Expose
gson annotation for fields which I need in the json, other fields are excluded.

- 231
- 2
- 12
-
Thanks dude. This is worked for me. After a long research i finally found a way to get out from this error. – kepy97 Mar 05 '18 at 15:33
This is how i finally solved it in my case. This works at least with Gson & 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;
}

- 11,565
- 12
- 43
- 70
if you are using spring boot,Jackson throws error while creating response from circular/bidirectional data, so use
@JsonIgnoreProperties
to ignore circularity
At Parent:
@OneToMany(mappedBy="dbApp")
@JsonIgnoreProperties("dbApp")
private Set<DBQuery> queries;
At child:
@ManyToOne
@JoinColumn(name = "db_app_id")
@JsonIgnoreProperties("queries")
private DBApp dbApp;

- 720
- 7
- 8
If you are using Javascript, there's a very easy solution to that using the replacer
parameter of JSON.stringify()
method where you can pass a function to modify the default serialization behavior.
Here's how you can use it. Consider the below example 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 the cyclic references by parsing the serialized data and modifying the next
property to point to the actual object if it's using a named reference with a @
like in this example.

- 723
- 6
- 11
Jackson provides JsonIdentityInfo
annotation to prevent circular references. You can check the tutorial here.

- 3,396
- 3
- 31
- 42
This error can appened when you have two objects :
class object1{
private object2 o2;
}
class object2{
private object1 o1;
}
With using GSon for serialization, i have got this error :
java.lang.IllegalStateException: circular reference error
Offending field: o1
To solved this, just add key word transient :
class object1{
private object2 o2;
}
class object2{
transient private object1 o1;
}
As you can see here : Why does Java have transient fields?
The transient keyword in Java is used to indicate that a field should not be serialized.

- 1
- 1

- 16,507
- 12
- 93
- 99
If you use GSON to convert Java class in JSON you can avoid the fields that cause the circular reference and the infinitive loop, you only have to put the annotation @Expose in the fields that you want to appear in the JSON, and the fields without the annotation @Expose do not appear in the JSON.
The circular reference appears for example if we try to serialize the class User with the field routes of class Route, and the class Route have the field user of the class User, then GSON try to serialize the class User and when try to serialize routes, serialize the class Route and in the class Route try to serialize the field user, and again try to serialize the class User, there is a circular reference that provoke the infinitive loop. I show the class User and Route that mentioned.
import com.google.gson.annotations.Expose;
Class User
@Entity
@Table(name = "user")
public class User {
@Column(name = "name", nullable = false)
@Expose
private String name;
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
@OnDelete(action = OnDeleteAction.CASCADE)
private Set<Route> routes;
@ManyToMany(fetch = FetchType.EAGER)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinTable(name = "like_", joinColumns = @JoinColumn(name = "id_user"),
inverseJoinColumns = @JoinColumn(name = "id_route"),
foreignKey = @ForeignKey(name = ""),
inverseForeignKey = @ForeignKey(name = ""))
private Set<Route> likes;
Class Route
@Entity
@Table(name = "route")
public class Route {
@ManyToOne()
@JoinColumn(nullable = false, name = "id_user", foreignKey =
@ForeignKey(name = "c"))
private User user;
To avoid the infinitive loop, we use the annotation @Expose that offer GSON.
I show in format JSON the result of serialize with GSON the class User.
{
"name": "ignacio"
}
We can see that the field route and likes do not exist in the format JSON, only the field name. Because of this, the circular reference is avoid.
If we want to use that, we have to create an object GSON on a specific way.
Gson converterJavaToJson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();
In the end, we transform the java class of the model of hibernate user using the conversor GSON created.
User user = createUserWithHibernate();
String json = converterJavaToJson.toJson(user);

- 151
- 1
- 4
the answer number 8 is the better, i think so if you know what field is throwing a error the you only set the fild in null and solved.
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 big comment is moved from the comment // **
to below.
java.lang.StackOverflowError [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError) (through reference chain: com.service.pegazo.bo.RequestMessageProfessional["requestMessage"]->com.service.pegazo.bo.RequestMessage["requestMessageProfessionals"]
-
1There is no "answer number 8", you would be better to give the name of the author of the referenced answer. The text you posted here was unreadable, please look at how answers are posted and try to lay them out neatly. Finally I do not understand how this answers the original question. Please add more details to explain the answer. – AdrianHHH Nov 20 '15 at 12:16
For example, ProductBean has got serialBean. The mapping would be bi-directional relationship. If we now try to use gson.toJson()
, it will end up with circular reference. In order to avoid that problem, you can follow these steps:
- Retrieve the results from datasource.
- Iterate the list and make sure the serialBean is not null, and then
- Set
productBean.serialBean.productBean = null;
- Then try to use
gson.toJson();
That should solve the problem

- 6,756
- 6
- 37
- 52

- 19