gson.toJson()抛出StackOverflowError

nim*_*rod 75 java stack-overflow json object gson

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

Gson gson = new Gson();
String json = gson.toJson(item);
Run Code Online (Sandbox Code Playgroud)

每次我尝试这样做,我都会收到此错误:

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)
Run Code Online (Sandbox Code Playgroud)

这些是我的BomItem类的属性:

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;
Run Code Online (Sandbox Code Playgroud)

我引用的BomModule类的属性:

private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
Run Code Online (Sandbox Code Playgroud)

知道导致这个错误的原因吗?我该如何解决?

SLa*_*aks 71

那个问题是你有一个循环引用.

BomModule您引用的类中:

private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
Run Code Online (Sandbox Code Playgroud)

BomModule显然,GSON完全不喜欢这种自我引用.

解决方法是将模块设置null为避免递归循环.这样我可以避免StackOverFlow-Exception.

item.setModules(null);
Run Code Online (Sandbox Code Playgroud)

或者使用关键字标记您不希望在序列化的json中显示的字段transient,例如:

private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;
Run Code Online (Sandbox Code Playgroud)


Zar*_*Zar 28

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

private Logger logger = Logger.getLogger(Foo.class);
Run Code Online (Sandbox Code Playgroud)

这可以通过制作记录器static或简单地将其移动到实际功能中来解决.

  • 非常棒的捕获.对于班级的自我引用显然根本不受GSON的喜爱.救了我很多头痛!+1 (4认同)

Bre*_*eno 20

如果你正在使用Realm并且你得到这个错误,并且给出麻烦的对象扩展了RealmObject,不要忘记realm.copyFromRealm(myObject)在传递给GSON进行序列化之前创建没有所有Realm绑定的副本.

我错过了这样做只是为了复制一堆对象中的一个...我花了很多时间才意识到堆栈跟踪没有命名对象类/类型.事实是,问题是由循环引用引起的,但它是RealmObject基类中的某个循环引用,而不是你自己的子类,这使得它更难被发现!


小智 12

正如SLaks所说,如果你的对象中有循环引用,就会发生StackOverflowError.

要修复它,您可以使用TypeAdapter作为对象.

例如,如果您只需要从对象生成String,则可以使用这样的适配器:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

并注册如下:

Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();
Run Code Online (Sandbox Code Playgroud)

或者像这样,如果您有接口并且想要为其所有子类使用适配器:

Gson gson = new GsonBuilder()
               .registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>())
               .create();
Run Code Online (Sandbox Code Playgroud)


ffo*_*onz 8

我的回答有点迟了,但我认为这个问题还没有很好的解决方案.我原来在这里找到它.

随着GSON你可以标记你的字段希望被包括在使用JSON @Expose是这样的:

@Expose
String myString;  // will be serialized as myString
Run Code Online (Sandbox Code Playgroud)

并使用以下命令创建gson对象:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Run Code Online (Sandbox Code Playgroud)

您只是公开的循环引用.这对我有用!