GSON: how to prevent StackOverflowError while keeping circular references?

后端 未结 2 2003
日久生厌
日久生厌 2020-12-01 15:17

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

相关标签:
2条回答
  • 2020-12-01 15:26

    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();
    
    0 讨论(0)
  • 2020-12-01 15:42

    I know this question has a few years now, but I'd like to contribute with my solution. Although @fdreger's answer is completely valid in case you want to exclude a field always, it doesn't work if you want to exclude it just in certain cases, avoiding this way the recursion. The way I approached the problem is:

    1. I write my own JsonSerializer. In it, I define a static variable to control de number of times an object of this same class is serialize and depending on the value, the object can be serialized or not.

       import com.fasterxml.jackson.core.JsonGenerator;
       import com.fasterxml.jackson.core.JsonProcessingException;
       import com.fasterxml.jackson.databind.JsonSerializer;
       import com.fasterxml.jackson.databind.SerializerProvider;
       import java.io.IOException;
      
       public class UserJsonSerializer extends JsonSerializer<User> {
      
           private static final ThreadLocal<Integer> depth = new ThreadLocal<Integer>() {
              @Override
              protected Integer initialValue() {
                  return 0;
              }
           };
      
           @Override
           public void serialize(User user, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
              // Here, we limit the number of instances to return. In this case, just 1.
      
              depth.set(depth.get() + 1);
              if(depth.get() >= 1) {
                  generator.writeNull();
              } else {
                  generator.writeObject(user);
              }
           }
      
           public static void clear() {
              depth.remove();
           }
      }
      
    2. Bind the UserJsonSerializer to the class you want to control

      public class SomeClass implements Serializable {
          @JsonSerialize(using = UserJsonSerializer.class)
          private User user;
      
         //...others fields...
      }
      
    3. Don't forget to call UserJsonSerializer#clear() method to reinitialize the counter everytime you're going to parse a new entity.

    I hope this helps.

    0 讨论(0)
提交回复
热议问题