简体   繁体   English

gson.toJson() 抛出 StackOverflowError

[英]gson.toJson() throws StackOverflowError

I would like to generate a JSON String from my object:我想从我的对象生成一个 JSON 字符串:

Gson gson = new Gson();
String json = gson.toJson(item);

Everytime I try to do this, I get this error:每次我尝试这样做时,都会收到此错误:

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)

These are the attributes of my BomItem class:这些是我的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;

Attributes of my referenced BomModule class:我引用的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;

Any idea what causes this error?知道是什么导致了这个错误吗? How can I fix it?我该如何解决?

That problem is that you have a circular reference.那个问题是你有一个循环引用。

In the BomModule class you are referencing to:BomModule类中,您引用了:

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

That self reference to BomModule , obviously, not liked by GSON at all.显然,GSON 根本不喜欢对BomModule自我引用。

A workaround is just set the modules to null to avoid the recursive looping.解决方法是将模块设置为null以避免递归循环。 This way I can avoid the StackOverFlow-Exception.这样我就可以避免 StackOverFlow-Exception。

item.setModules(null);

Or mark the fields you don't want to show up in the serialized json by using the transient keyword, eg:或者使用transient关键字标记您不想在序列化json中显示的字段,例如:

private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;

I had this problem when I had a Log4J logger as a class property, such as:当我将 Log4J 记录器作为类属性时,我遇到了这个问题,例如:

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

This can be solved by either making the logger static or simply by moving it into the actual function(s).这可以通过使记录器static或简单地将其移动到实际函数中来解决。

If you're using Realm and you get this error, and the object giving the trouble extends RealmObject, don't forget to do realm.copyFromRealm(myObject) to create a copy without all the Realm bindings before passing through to GSON for serialization.如果您正在使用 Realm 并且遇到此错误,并且出现问题的对象扩展了 RealmObject,请不要忘记执行realm.copyFromRealm(myObject)在传递给 GSON 进行序列化之前创建一个没有所有 Realm 绑定的副本。

I'd missed doing this for just one amongst a bunch of objects being copied... took me ages to realise as the stack trace doesn't name the object class/type.我错过了为正在复制的一堆对象中的一个做这件事......花了我很长时间才意识到,因为堆栈跟踪没有命名对象类/类型。 Thing is, the issue is caused by a circular reference, but it's a circular reference somewhere in the RealmObject base class, not your own subclass, which makes it harder to spot!问题是,问题是由循环引用引起的,但它是 RealmObject 基类中某处的循环引用,而不是您自己的子类,这使得它更难发现!

As SLaks said StackOverflowError happen if you have circular reference in your object.正如 SLaks 所说,如果您的对象中有循环引用,就会发生 StackOverflowError。

To fix it you could use TypeAdapter for your object.要修复它,您可以为您的对象使用 TypeAdapter。

For example, if you need only generate String from your object you could use adapter like this:例如,如果你只需要从你的对象生成 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());
    }
}

and register it like this:并像这样注册:

Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();

or like this, if you have interface and want to use adapter for all its subclasses:或者像这样,如果您有接口并希望为其所有子类使用适配器:

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

My answer is a little bit late, but I think this question doesn't have a good solution yet.我的回答有点晚了,但我认为这个问题还没有很好的解决方案。 I found it originally here .我最初是在这里找到的。

With Gson you can mark the fields you do want to be included in json with @Expose like this:随着GSON你可以标记想被列入使用JSON领域@Expose是这样的:

@Expose
String myString;  // will be serialized as myString

and create the gson object with:并使用以下命令创建 gson 对象:

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

Circular references you just do not expose.您只是公开的循环引用。 That did the trick for me!这对我有用!

This error is common when you have a logger in your super class.当您的超类中有记录器时,此错误很常见。 As @Zar suggested before, you can use static for your logger field, but this also works:正如@Zar 之前建议的那样,您可以对记录器字段使用静态,但这也有效:

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

PS probably it will work and with @Expose annotation check more about this here: https://stackoverflow.com/a/7811253/1766166 PS 可能它会起作用,并使用 @Expose 注释在此处检查更多相关信息: https ://stackoverflow.com/a/7811253/1766166

I have the same problem.我也有同样的问题。 In my case the reason was that constructor of my serialized class take context variable, like this:就我而言,原因是我的序列化类的构造函数采用上下文变量,如下所示:

public MetaInfo(Context context)

When I delete this argument, error has gone.当我删除这个参数时,错误消失了。

public MetaInfo()

Edit: Sorry for my bad, this is my first answer.编辑:对不起,这是我的第一个答案。 Thanks for your advises.谢谢你的建议。

I create my own Json Converter我创建了自己的 Json 转换器

The main solution I used is to create a parents object set for each object reference.我使用的主要解决方案是为每个对象引用创建一个父对象集。 If a sub-reference points to existed parent object, it will discard.如果子引用指向存在的父对象,它将被丢弃。 Then I combine with an extra solution, limiting the reference time to avoid infinitive loop in bi-directional relationship between entities.然后我结合了一个额外的解决方案,限制参考时间以避免实体之间双向关系中的无限循环。

My description is not too good, hope it helps you guys.我的描述不太好,希望对大家有帮助。

This is my first contribution to Java community (solution to your problem).这是我对 Java 社区的第一次贡献(解决您的问题)。 You can check it out ;) There is a README.md file https://github.com/trannamtrung1st/TSON你可以看看;) 有一个 README.md 文件https://github.com/trannamtrung1st/TSON

For Android users, you cannot serialize a Bundle due to a self-reference to Bundle causing a StackOverflowError .对于Android用户,你不能序列化Bundle由于自我参照Bundle造成StackOverflowError

To serialize a bundle, register a BundleTypeAdapterFactory .要序列化包,请注册一个BundleTypeAdapterFactory

In Android, gson stack overflow turned out to be the declaration of a Handler.在Android中,gson堆栈溢出原来是一个Handler的声明。 Moved it to a class that isn't being deserialized.将其移至未反序列化的类。

Based on Zar's recommendation, I made the the handler static when this happened in another section of code.根据 Zar 的建议,当这发生在另一段代码中时,我将处理程序设为静态。 Making the handler static worked as well.使处理程序静态也有效。

BomItem refers to BOMModule ( Collection<BomModule> modules ), and BOMModule refers to BOMItem ( Collection<BomItem> items ). BomItem指的是BOMModule ( Collection<BomModule> modules ),而BOMModule指的是BOMItem ( Collection<BomItem> items )。 Gson library doesn't like circular references. Gson 库不喜欢循环引用。 Remove this circular dependency from your class.从您的类中删除此循环依赖项。 I too had faced same issue in the past with gson lib.过去我也遇到过与 gson lib 相同的问题。

I had this problem occur for me when I put:当我放置时,我遇到了这个问题:

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

in my object...which made perfect sense after an hour or so of debugging!在我的对象中......经过一个小时左右的调试后,这非常有意义!

Avoid unnecessary workarounds, like setting values to null or making fields transient.避免不必要的解决方法,例如将值设置为 null 或使字段变得瞬态。 The right way to do this, is to annotate one of the fields with @Expose and then tell Gson to serialize only the fields with the annotation:正确的方法是用@Expose 注释其中一个字段,然后告诉 Gson 只序列化带有注释的字段:

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

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

I had a similar issue where the class had an InputStream variable which I didn't really have to persist.我有一个类似的问题,该类有一个 InputStream 变量,我实际上不必坚持。 Hence changing it to Transient solved the issue.因此将其更改为 Transient 解决了该问题。

After some time fighting with this issue, I believe i have a solution.经过一段时间与这个问题的斗争,我相信我有一个解决方案。 Problem is in unresolved bidirectional connections, and how to represent connections when they are being serialized.问题在于未解决的双向连接,以及如何在序列化时表示连接。 The way to fix that behavior is to "tell" gson how to serialize objects.修复该行为的方法是“告诉” gson如何序列化对象。 For that purpose we use Adapters .为此,我们使用Adapters

By using Adapters we can tell gson how to serialize every property from your Entity class as well as which properties to serialize.通过使用Adapters我们可以告诉gson如何序列化Entity类中的每个属性以及序列化哪些属性。

Let Foo and Bar be two entities where Foo has OneToMany relation to Bar and Bar has ManyToOne relation to Foo .FooBar是两个实体,其中Foo具有OneToMany的关系,以BarBarManyToOne关系Foo We define Bar adapter so when gson serializes Bar , by defining how to serialize Foo from perspective of Bar cyclic referencing will not be possible.我们定义了Bar适配器,所以当gson序列化Bar ,通过定义如何从Bar循环引用的角度序列化Foo将是不可能的。

public class BarAdapter implements JsonSerializer<Bar> {
    @Override
    public JsonElement serialize(Bar bar, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("id", bar.getId());
        jsonObject.addProperty("name", bar.getName());
        jsonObject.addProperty("foo_id", bar.getFoo().getId());
        return jsonObject;
    }
}

Here foo_id is used to represent Foo entity which would be serialized and which would cause our cyclic referencing problem.这里foo_id用于表示将被序列化并会导致我们的循环引用问题的Foo实体。 Now when we use adapter Foo will not be serialized again from Bar only its id will be taken and put in JSON .现在当我们使用适配器Foo将不会从Bar再次序列化,只会获取它的 id 并将其放入JSON Now we have Bar adapter and we can use it to serialize Foo .现在我们有了Bar适配器,我们可以用它来序列化Foo Here is idea:这是想法:

public String getSomething() {
    //getRelevantFoos() is some method that fetches foos from database, and puts them in list
    List<Foo> fooList = getRelevantFoos();

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Bar.class, new BarAdapter());
    Gson gson = gsonBuilder.create();

    String jsonResponse = gson.toJson(fooList);
    return jsonResponse;
}

One more thing to clarify, foo_id is not mandatory and it can be skipped.还有一件事要澄清, foo_id不是强制性的,可以跳过。 Purpose of adapter in this example is to serialize Bar and by putting foo_id we showed that Bar can trigger ManyToOne without causing Foo to trigger OneToMany again...在这个例子中适配器的目的是序列化Bar并且通过放置foo_id我们展示了Bar可以触发ManyToOne而不会导致Foo再次触发OneToMany ...

Answer is based on personal experience, therefore feel free to comment, to prove me wrong, to fix mistakes, or to expand answer.答案是基于个人经验,因此请随时发表评论,证明我错了,纠正错误,或扩大答案。 Anyhow I hope someone will find this answer useful.无论如何,我希望有人会发现这个答案很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM