15
Imagine that I have these classes:
public class A {
private B b;
}
public class B {
private A a;
}
And then I have this:
A a = new A();
B b = new B();
a.b = b;
b.a = a;
Gson gson = new Gson();
String x = gson.toJson(a);
The result is a StackOverflowError
(I truncated it, but it consists of a lot of repetitions of what is below):
java.lang.StackOverflowError at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source) at sun.reflect.UnsafeObjectFieldAccessorImpl.get(Unknown Source) at java.lang.reflect.Field.get(Unknown Source) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.writeField(ReflectiveTypeAdapterFactory.java:124) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:238) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240) at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:950) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240) at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:950) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:113) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:240) at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:950)
There are several ways to resolve the circular reference in GSON, such as adding the modifier transient
, use some exclusion strategy, etc. However all these forms only work after I already know where is the circular reference.
I happen to have a scenario where there is a set with a few hundred distinct classes related to being serialized in JSON format through GSON. In the middle of all these objects, there’s at least one circular reference somewhere, but locating it is like looking for a needle in a haystack. I know that once you have identified the circular reference, solving it should be relatively easy. How do I find out where this circular reference is?
Ah, and I’m on a project that’s in maintenance and in production, so I can’t just decide to replace a lot of technologies with other technologies, introduce or delete libraries or redesign a lot of things. If so, I would use Jackson in place of GSON, since Jackson clearly says where the circular reference is when it occurs.
I’m using GSON 2.5.
You could add Jackson to the dependencies in order to find the point or points where circular reference occurs, after this go back to Gson and add an exclusion strategy to these points
– brow-joe
If you think valid I can put some ways to apply exclusion strategy with Gson
– brow-joe
@Brow-joe I’m gonna try this thing of putting Jackson on just to track down the circular references. As for the exclusion strategy, I believe this is a topic for another question.
– Victor Stafusa
Good afternoon friend to see https://github.com/google/gson/issues/440 a possible solution to your problem is a downgrade to version 1.7.1 tries to follow this line, good luck
– Evandro Mendes
I don’t think it’s the best solution, but a workaround would be to add a cyclic referencing plugin to your IDE, which might help you find the problem. Ex for eclipse: https://marketplace.eclipse.org/content/stan-structure-analysis-java#group-Details
– clappis
If you are using Intellij, there is this plugin for example: https://plugins.jetbrains.com/plugin/93-metricsreloaded. that seems to have a profile for this, identify relationships between classes, you can check. The part of this, if it is impossible to mark all as transient and go analyzing, or put a max deep ....
– Brother
An alternate mode, but fast is: If you are using spring. Puts a @Component in all classes and tries to move up the application, then spring will tell which class depends on the other. : D
– Calixto
Does this depend on the status of the objects? Or is it because there is a cyclic class dependency graph?
– Jefferson Quesado
@Jeffersonquesado I no longer work in the company that made this system two and a half years ago. But yes, it depended on the status of the objects' internal data.
– Victor Stafusa