GSON:如何在保持循环引用的同时防止StackOverflowError?
我有一个带循环引用的结构。 出于调试目的,我想转储它。 基本上任何格式,但我选择了JSON。
因为它可以是任何类,所以我选择了不需要JAXB注释的GSON。
但是GSON命中循环引用并递归直到StackOverflowError
。
我怎样才能限制GSON
-
忽略某些class级成员?
@XmlTransient
和@JsonIgnore
都没有服从。 -
忽略某些对象图路径? 例如,我可以指示GSON不要序列化
release.customFields.product
。 -
去最多2个级别的深度?
相关: Gson.toJson给出了StackOverFlowError,在这种情况下如何获得正确的json? (公共静态类)
只需使字段成为瞬态(如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"; // ...
要使用自定义ExclusionStrategy,请使用构建器构建Gson对象:
Gson g = new GsonBuilder() .setExclusionStrategies(new MyOwnExclusionStrategy()) .create();
我知道这个问题已经有几年了,但我想为我的解决方案做出贡献。 虽然@fdreger的答案在你想要总是排除一个字段的情况下是完全有效的,但如果你想在某些情况下排除它,它就不起作用,避免这种方式递归。 我解决问题的方法是:
-
我写自己的
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
{ private static final ThreadLocal depth = new ThreadLocal () { @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(); } } -
将
UserJsonSerializer
绑定到要控制的类public class SomeClass implements Serializable { @JsonSerialize(using = UserJsonSerializer.class) private User user; //...others fields... }
-
每次要解析新实体时,不要忘记调用
UserJsonSerializer#clear()
方法重新初始化计数器。
我希望这有帮助。