12

我有一个带有循环引用的结构。出于调试目的,我想转储它。基本上和任何格式一样,但我选择了 JSON。

因为它可以是任何类,所以我选择了不需要 JAXB 注释的 GSON。

但是 GSON 命中循环引用并递归直到StackOverflowError.

我如何将 GSON 限制为

  • 忽略某些班级成员?两者@XmlTransient皆不@JsonIgnore服从。

  • 忽略某些对象图路径?例如,我可以指示 GSON 不要序列化release.customFields.product​​.

  • 到最多2个级别的深度?

相关:Gson.toJson 给出 StackOverFlowError,在这种情况下如何获得正确的 json?(公共静态类)

4

2 回答 2

24

只需使字段瞬态(如中private transient int field = 4;)。GSON 明白这一点。

编辑

无需内置注解;Gson 允许您插入自己的策略来排除字段和类。它们不能基于路径或嵌套级别,但注释和名称很好。

如果我想跳过“my.model.Person”类中名为“lastName”的字段,我可以编写如下排除策略:

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;
    }
}

我也可以自己做注释:

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

}

并将方法重写shouldSkipField为:

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

这将使我能够执行以下操作:

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

    // ...

要使用自定义排除策略,请使用构建器构建 Gson 对象:

Gson g = new GsonBuilder()
       .setExclusionStrategies(new MyOwnExclusionStrategy())
       .create();
于 2013-01-23T21:16:40.853 回答
0

我知道这个问题已经有几年了,但我想为我的解决方案做出贡献。尽管@fdreger 的答案在您想总是排除一个字段的情况下是完全有效的,但如果您只想在某些情况下排除它,它就不起作用,避免这种递归。我解决问题的方法是:

  1. 我自己写JsonSerializer。在其中,我定义了一个静态变量来控制同一个类的对象被序列化的次数,并且根据值,对象可以被序列化或不被序列化。

     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. 绑定UserJsonSerializer到你要控制的类

    public class SomeClass implements Serializable {
        @JsonSerialize(using = UserJsonSerializer.class)
        private User user;
    
       //...others fields...
    }
    
  3. UserJsonSerializer#clear()每次要解析新实体时,不要忘记调用方法来重新初始化计数器。

我希望这有帮助。

于 2017-11-28T07:15:40.600 回答