97

オブジェクトから 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)

これらは、私の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;

参照した 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;

このエラーの原因は何ですか? どうすれば修正できますか?

4

15 に答える 15

93

その問題は、循環参照があることです。

あなたが参照しているBomModuleクラスでは:

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

への自己参照はBomModule、明らかに、GSON にはまったく好まれていません。

回避策は、モジュールを に設定してnull、再帰ループを回避することです。このようにして、StackOverFlow-Exception を回避できます。

item.setModules(null);

または、キーワードを使用して、シリアル化された json に表示したくないフィールドをマークします。例:transient

private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;
于 2012-04-18T13:05:05.580 に答える
31

次のようなクラス プロパティとして Log4J ロガーがあるときに、この問題が発生しました。

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

これは、ロガーを作成するか、static単に実際の関数に移動することで解決できます。

于 2013-01-28T22:02:16.700 に答える
27

realm.copyFromRealm(myObject)Realm を使用していて、このエラーが発生し、問題を引き起こしているオブジェクトが RealmObject を拡張している場合は、シリアル化のために GSON に渡す前に、すべての Realm バインディングなしでコピーを作成することを忘れないでください。

コピーされているオブジェクトの束の中の 1 つだけに対してこれを行うのを見逃していました... スタック トレースがオブジェクト クラス/タイプに名前を付けていないため、気付くのに時間がかかりました。問題は循環参照が原因ですが、それは自分のサブクラスではなく、RealmObject 基底クラスのどこかで循環参照であるため、見つけにくくなっています!

于 2016-09-23T22:36:20.160 に答える
3

このエラーは、スーパー クラスにロガーがある場合によく発生します。@Zar が以前に提案したように、ロガー フィールドにstaticを使用できますが、これも機能します。

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

PS おそらく動作し、@Expose アノテーションを使用して、これについて詳しく確認してください: https://stackoverflow.com/a/7811253/1766166

于 2015-05-22T07:09:39.233 に答える
0

この問題としばらく戦った後、解決策があると思います。問題は、未解決の双方向接続と、シリアル化されているときに接続を表す方法にあります。その動作を修正する方法は、gsonオブジェクトをシリアル化する方法を「伝える」ことです。そのために、 を使用しますAdapters

を使用することで、クラスのすべてのプロパティをシリアル化する方法と、シリアル化するプロパティAdaptersを指定できます。gsonEntity

と に関係がある とに関係があるFoo2つのBarエンティティとします。アダプターを定義するので、シリアライズするときに、循環参照の観点からシリアライズする方法を定義することはできません。FooOneToManyBarBarManyToOneFooBargsonBarFooBar

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を表すために使用されます。Fooこれで、アダプターを使用すると、その ID のみが取得されて入れられるため、アダプターFooが再度シリアル化されることはありません。これでアダプターができたので、それを使用して. ここにアイデアがあります:BarJSONBarFoo

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

明確にするもう 1 つのことfoo_idは、必須ではなく、スキップできます。この例のアダプターの目的はシリアライズBarすることであり、配置することで、再度トリガーすることなくトリガーできることfoo_idを示しました...BarManyToOneFooOneToMany

回答は個人的な経験に基づいているため、コメントしたり、私が間違っていることを証明したり、間違いを修正したり、回答を拡大したりしてください. とにかく、誰かがこの答えが役立つことを願っています。

于 2020-03-08T20:58:32.493 に答える