Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GSON: how to prevent StackOverflowError while keeping circular references?

I have a structure with circular references. And for debug purposes, I want to dump it. Basically as any format, but I chose JSON.

Since it can be any class, I chose GSON which doesn't needs JAXB annotations.

But GSON hits the circular references and recurses until StackOverflowError.

How can I limit GSON to

  • ignore certain class members? Both @XmlTransient and @JsonIgnore are not obeyed.

  • ignore certain object graph paths? E.g. I could instruct GSON not to serialize release.customFields.product.

  • go to the depth of at most 2 levels?

Related: Gson.toJson gives StackOverFlowError, how to get proper json in this case? (public static class)

like image 410
Ondra Žižka Avatar asked Jan 23 '13 21:01

Ondra Žižka


1 Answers

Simply make the fields transient (as in private transient int field = 4;). GSON understands that.

Edit

No need for a built-in annotation; Gson lets you plug in your own strategies for excluding fields and classes. They cannot be based on a path or nesting level, but annotations and names are fine.

If I wanted to skip fields that are named "lastName" on class "my.model.Person", I could write an exclusion strategy like this:

class MyExclusionStrategy implements ExclusionStrategy {

    public boolean shouldSkipField(FieldAttributes fa) {                
        String className = fa.getDeclaringClass().getName();
        String fieldName = fa.getName();
        return 
            className.equals("my.model.Person")
                && fieldName.equals("lastName");
    }

    @Override
    public boolean shouldSkipClass(Class<?> type) {
        // never skips any class
        return false;
    }
}

I could also make my own annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface GsonRepellent {

}

And rewrite the shouldSkipField method as:

public boolean shouldSkipField(FieldAttributes fa) {
    return fa.getAnnotation(GsonRepellent.class) != null;
}

This would enable me to do things like:

public class Person {
    @GsonRepellent
    private String lastName = "Troscianko";

    // ...

To use a custom ExclusionStrategy, build Gson object using the builder:

Gson g = new GsonBuilder()
       .setExclusionStrategies(new MyOwnExclusionStrategy())
       .create();
like image 168
fdreger Avatar answered Sep 18 '22 15:09

fdreger