gson.toJson() throws StackOverflowError

That problem is that you have a circular reference.

In the BomModule class you are referencing to:

private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;

That self reference to BomModule, obviously, not liked by GSON at all.

A workaround is just set the modules to null to avoid the recursive looping. This way I can avoid the StackOverFlow-Exception.

item.setModules(null);

Or mark the fields you don't want to show up in the serialized json by using the transient keyword, eg:

private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;

I had this problem when I had a Log4J logger as a class property, such as:

private Logger logger = Logger.getLogger(Foo.class);

This can be solved by either making the logger static or simply by moving it into the actual function(s).


If you're using Realm and you get this error, and the object giving the trouble extends RealmObject, don't forget to do realm.copyFromRealm(myObject) to create a copy without all the Realm bindings before passing through to GSON for serialization.

I'd missed doing this for just one amongst a bunch of objects being copied... took me ages to realise as the stack trace doesn't name the object class/type. Thing is, the issue is caused by a circular reference, but it's a circular reference somewhere in the RealmObject base class, not your own subclass, which makes it harder to spot!


As SLaks said StackOverflowError happen if you have circular reference in your object.

To fix it you could use TypeAdapter for your object.

For example, if you need only generate String from your object you could use adapter like this:

class MyTypeAdapter<T> extends TypeAdapter<T> {
    public T read(JsonReader reader) throws IOException {
        return null;
    }

    public void write(JsonWriter writer, T obj) throws IOException {
        if (obj == null) {
            writer.nullValue();
            return;
        }
        writer.value(obj.toString());
    }
}

and register it like this:

Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();

or like this, if you have interface and want to use adapter for all its subclasses:

Gson gson = new GsonBuilder()
               .registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>())
               .create();