Java toJson()抛出StackOverflowerError

Java toJson()抛出StackOverflowerError,java,json,object,gson,stack-overflow,Java,Json,Object,Gson,Stack Overflow,我想从我的对象生成一个JSON字符串: Gson gson = new Gson(); String json = gson.toJson(item); 每次我尝试这样做时,都会出现以下错误: 14:46:40,236 ERROR [[BomItemToJSON]] Servlet.service() for servlet BomItemToJSON threw exception java.lang.StackOverflowError at com.google.gson.str

我想从我的对象生成一个JSON字符串:

Gson gson = new Gson();
String json = gson.toJson(item);
每次我尝试这样做时,都会出现以下错误:

14:46:40,236 ERROR [[BomItemToJSON]] Servlet.service() for servlet BomItemToJSON threw exception
java.lang.StackOverflowError
    at com.google.gson.stream.JsonWriter.string(JsonWriter.java:473)
    at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:347)
    at com.google.gson.stream.JsonWriter.value(JsonWriter.java:440)
    at com.google.gson.internal.bind.TypeAdapters$7.write(TypeAdapters.java:235)
    at com.google.gson.internal.bind.TypeAdapters$7.write(TypeAdapters.java:220)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:200)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
    at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:843)
以下是myBomItem类的属性:

private int itemId;
private Collection<BomModule> modules;
private boolean deprecated;
private String partNumber;
private String description; //LOB
private int quantity;
private String unitPriceDollar;
private String unitPriceEuro;
private String discount; 
private String totalDollar;
private String totalEuro;
private String itemClass;
private String itemType;
private String vendor;
private Calendar listPriceDate;
private String unitWeight;
private String unitAveragePower;
private String unitMaxHeatDissipation;
private String unitRackSpace;
private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
private int itemId;
私人收藏模块;
私有布尔不赞成;
私有字符串部件号;
私有字符串描述//高球
私人整数数量;
私人字符串单位美元;
私人字符串单位欧元;
私人字符串折扣;
私用美元;
欧元私人债券;
私有字符串类;
私有字符串itemType;
私人字符串供应商;
私人日历日期;
私有字符串单位重量;
私人平均权力;
私有串单元散热;
私有字符串unitRackSpace;
我引用的BomModule类的属性:

private int itemId;
private Collection<BomModule> modules;
private boolean deprecated;
private String partNumber;
private String description; //LOB
private int quantity;
private String unitPriceDollar;
private String unitPriceEuro;
private String discount; 
private String totalDollar;
private String totalEuro;
private String itemClass;
private String itemType;
private String vendor;
private Calendar listPriceDate;
private String unitWeight;
private String unitAveragePower;
private String unitMaxHeatDissipation;
private String unitRackSpace;
private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
private int moduleId;
私有字符串moduleName;
私有布尔isRootModule;
私人收藏模块;
私人收藏子模块;
私人收藏物品;
私人整数数量;

知道是什么导致了这个错误吗?如何修复它?

问题是您有一个循环引用

在所引用的
BomModule
类中:

private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
或者使用
transient
关键字标记您不希望在序列化json中显示的字段,例如:

private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;
私有瞬态采集模块;
私有瞬态采集子模块;

当我将Log4J记录器作为类属性时,我遇到了这个问题,例如:

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

这可以通过使记录器成为静态的或者简单地将其移动到实际的函数中来解决。

在Android中,gson堆栈溢出被证明是处理程序的声明。将其移动到未被反序列化的类


根据Zar的建议,当这发生在另一段代码中时,我将处理程序设置为静态。使处理程序保持静态也起作用。

正如SLaks所说,如果对象中有循环引用,则会发生StackOverflower错误

要修复它,可以对对象使用TypeAdapter

例如,如果只需要从对象生成字符串,则可以使用如下适配器:

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());
    }
}
Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();
public MetaInfo(Context context)
@Expose
String myString;  // will be serialized as myString
类MyTypeAdapter扩展了TypeAdapter{
公共T读取(JsonReader读取器)引发IOException{
返回null;
}
public void write(JsonWriter writer,T obj)抛出IOException{
if(obj==null){
writer.nullValue();
返回;
}
writer.value(obj.toString());
}
}
并按如下方式注册:

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());
    }
}
Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();
public MetaInfo(Context context)
@Expose
String myString;  // will be serialized as myString
Gson Gson=new GsonBuilder()
.registerTypeAdapter(BomItem.class,新的MyTypeAdapter())
.create();
或者像这样,如果您有一个接口并希望为其所有子类使用适配器:

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

我也有同样的问题。在我的例子中,原因是序列化类的构造函数接受上下文变量,如下所示:

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());
    }
}
Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();
public MetaInfo(Context context)
@Expose
String myString;  // will be serialized as myString
当我删除这个参数时,错误就消失了

public MetaInfo()

当超级类中有记录器时,此错误很常见。正如@Zar之前所建议的,您可以对记录器字段使用static,但这也适用于:

protected final transient Logger logger = Logger.getLogger(this.getClass());

另一方面,它可能会在@Expose annotation中起作用,并在此处检查更多信息:

BomItem
指的是
BOMModule
Collection modules
),而
BOMModule
指的是
BomItem
Collection items
)。Gson库不喜欢循环引用。从类中删除此循环依赖项。过去,我在使用gson lib时也遇到过同样的问题。

如果您使用的是Realm,并且出现此错误,并且导致问题的对象扩展了RealmObject,请不要忘记执行
Realm.copyFromRealm(myObject)
在传递到gson进行序列化之前创建一个没有所有领域绑定的副本


我错过了在一堆被复制的对象中的一个。。。我花了很长时间才意识到堆栈跟踪没有命名对象类/类型。问题是,这个问题是由循环引用引起的,但它是RealmObject基类中某个地方的循环引用,而不是您自己的子类,这使得它更难发现

我的回答有点晚了,但我认为这个问题还没有一个好的解决方案。我原来找到的

使用Gson,您可以使用
@Expose
标记您希望包含在json中的字段,如下所示:

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());
    }
}
Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();
public MetaInfo(Context context)
@Expose
String myString;  // will be serialized as myString
并使用以下命令创建gson对象:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

不公开的循环引用。这对我来说是个好办法

我在放置以下内容时遇到了此问题:

Logger logger = Logger.getLogger( this.getClass().getName() );

在我的对象中…经过一个小时左右的调试,这是非常有意义的

编辑:对不起我的不好,这是我的第一个答案。谢谢你的建议

我创建自己的Json转换器

我使用的主要解决方案是为每个对象引用创建父对象集。如果子引用指向现有的父对象,它将放弃。 然后,我结合一个额外的解决方案,限制参考时间,以避免实体之间双向关系中的不定式循环

我的描述不太好,希望对你们有帮助

这是我对Java社区(解决您的问题)的第一个贡献。您可以查看它;) 有一个README.md文件

对于Android用户,您无法序列化
捆绑包
,因为对
捆绑包的自引用导致
堆栈溢出错误


要序列化捆绑包,.

避免不必要的解决方法,如将值设置为null或使字段变为临时字段。正确的方法是使用@Expose注释其中一个字段,然后告诉Gson仅使用注释序列化字段:

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

...
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
私有集合父模块;