How to find where is the circular reference in GSON?

Asked

Viewed 637 times

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.

  • 4

    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

  • If you think valid I can put some ways to apply exclusion strategy with Gson

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

  • 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

  • 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

  • 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 ....

  • 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

  • Does this depend on the status of the objects? Or is it because there is a cyclic class dependency graph?

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

Show 4 more comments

1 answer

1

The problem would be the fact that both objects are being set in the other and disturbs the construction of JSON, to solve the problem just add the annotation @JsonIgnore in one of the class Atrributos. If you want to ignore the value of the type variable A in class B, just add the annotation above the attribute statement. Example:

public class B {
    @JsonIgnore
    private A a;
}

Remembering that, when solving this way, the JSON of class A would come with the data of class B but B would be returned without the reference of class A.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.