Saya ingin membuat String JSON dari objek saya:
Gson gson = new Gson();
String json = gson.toJson(item);
Setiap kali saya mencoba melakukan ini, saya mendapatkan kesalahan ini:
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)
Ini adalah atribut kelas BomItem saya :
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;
Atribut kelas BomModule yang saya rujuk :
private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
Tahu apa yang menyebabkan kesalahan ini? Bagaimana cara memperbaikinya?
JsonWriter.java:473)
, bagaimana mengidentifikasi akar penyebab Gson stackoverflowJawaban:
Masalahnya adalah Anda memiliki referensi melingkar.
Di
BomModule
kelas yang Anda rujuk ke:private Collection<BomModule> parentModules; private Collection<BomModule> subModules;
Referensi diri itu
BomModule
, jelas, sama sekali tidak disukai oleh GSON.Solusinya hanya mengatur modul
null
untuk menghindari perulangan rekursif. Dengan cara ini saya dapat menghindari StackOverFlow-Exception.item.setModules(null);
Atau tandai kolom yang tidak ingin Anda tampilkan di serialized json dengan menggunakan
transient
kata kunci, misalnya:private transient Collection<BomModule> parentModules; private transient Collection<BomModule> subModules;
sumber
Saya mengalami masalah ini ketika saya memiliki logger Log4J sebagai properti kelas, seperti:
private Logger logger = Logger.getLogger(Foo.class);
Ini dapat diselesaikan dengan membuat logger
static
atau hanya dengan memindahkannya ke fungsi sebenarnya.sumber
Jika Anda menggunakan Realm dan mendapatkan kesalahan ini, dan objek yang memberikan masalah tersebut memperluas RealmObject, jangan lupa lakukan
realm.copyFromRealm(myObject)
untuk membuat salinan tanpa semua binding Realm sebelum meneruskan ke GSON untuk serialisasi.Saya melewatkan melakukan ini hanya untuk satu di antara sekumpulan objek yang sedang disalin ... butuh waktu lama untuk saya sadari karena jejak tumpukan tidak menyebutkan kelas / jenis objek. Masalahnya, masalahnya disebabkan oleh referensi melingkar, tetapi ini adalah referensi melingkar di suatu tempat di kelas dasar RealmObject, bukan subkelas Anda sendiri, yang membuatnya lebih sulit dikenali!
sumber
Seperti kata SLaks, StackOverflowError terjadi jika Anda memiliki referensi melingkar di objek Anda.
Untuk memperbaikinya Anda bisa menggunakan TypeAdapter untuk objek Anda.
Misalnya, jika Anda hanya perlu membuat String dari objek Anda, Anda dapat menggunakan adaptor seperti ini:
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()); } }
dan daftarkan seperti ini:
Gson gson = new GsonBuilder() .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>()) .create();
atau seperti ini, jika Anda memiliki antarmuka dan ingin menggunakan adaptor untuk semua subkelasnya:
Gson gson = new GsonBuilder() .registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>()) .create();
sumber
Jawaban saya agak terlambat, tetapi menurut saya pertanyaan ini belum memiliki solusi yang baik. Saya menemukannya awalnya di sini .
Dengan Gson Anda dapat menandai bidang yang ingin Anda masukkan ke dalam json dengan
@Expose
seperti ini:@Expose String myString; // will be serialized as myString
dan buat objek gson dengan:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Referensi melingkar yang tidak Anda ungkapkan. Itu berhasil untuk saya!
sumber
Kesalahan ini biasa terjadi ketika Anda memiliki logger di kelas super Anda. Seperti yang disarankan @Zar sebelumnya, Anda dapat menggunakan statis untuk bidang logger Anda, tetapi ini juga berfungsi:
protected final transient Logger logger = Logger.getLogger(this.getClass());
PS mungkin akan berfungsi dan dengan penjelasan @Expose, periksa lebih lanjut tentang ini di sini: https://stackoverflow.com/a/7811253/1766166
sumber
Saya memiliki masalah yang sama. Dalam kasus saya, alasannya adalah konstruktor kelas serial saya mengambil variabel konteks, seperti ini:
public MetaInfo(Context context)
Ketika saya menghapus argumen ini, kesalahan telah hilang.
public MetaInfo()
sumber
Edit: Maaf atas kesalahan saya, ini adalah jawaban pertama saya. Terima kasih atas saran Anda.
Saya membuat Konverter Json saya sendiri
Solusi utama yang saya gunakan adalah membuat set objek orang tua untuk setiap referensi objek. Jika sub-referensi menunjuk ke objek induk yang ada, itu akan dibuang. Kemudian saya kombinasikan dengan solusi ekstra, membatasi waktu referensi untuk menghindari loop infinitif dalam hubungan dua arah antar entitas.
Uraian saya kurang bagus, semoga bisa membantu kalian.
Ini adalah kontribusi pertama saya untuk komunitas Java (solusi untuk masalah Anda). Anda dapat memeriksanya;) Ada file README.md https://github.com/trannamtrung1st/TSON
sumber
Di Android, gson stack overflow ternyata adalah deklarasi Handler. Memindahkannya ke kelas yang tidak sedang deserialisasi.
Berdasarkan rekomendasi Zar, saya membuat pawang menjadi statis ketika ini terjadi di bagian kode lainnya. Membuat pawang statis juga berfungsi.
sumber
BomItem
mengacu padaBOMModule
(Collection<BomModule> modules
), danBOMModule
mengacu padaBOMItem
(Collection<BomItem> items
). Perpustakaan Gson tidak menyukai referensi melingkar. Hapus ketergantungan melingkar ini dari kelas Anda. Saya juga pernah menghadapi masalah yang sama di masa lalu dengan gson lib.sumber
Saya mengalami masalah ini ketika saya meletakkan:
Logger logger = Logger.getLogger( this.getClass().getName() );
dalam objek saya ... yang sangat masuk akal setelah satu jam atau lebih debugging!
sumber
Untuk pengguna Android, Anda tidak dapat membuat serial
Bundle
karena referensi mandiri yangBundle
menyebabkan aStackOverflowError
.Untuk membuat serial bundel, daftarkan a
BundleTypeAdapterFactory
.sumber
Hindari solusi yang tidak perlu, seperti menyetel nilai ke null atau membuat kolom menjadi sementara. Cara yang tepat untuk melakukannya adalah dengan memberi anotasi pada salah satu bidang dengan @Expose dan kemudian memberi tahu Gson untuk membuat serial hanya bidang dengan anotasi:
private Collection<BomModule> parentModules; @Expose private Collection<BomModule> subModules; ... Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
sumber
Saya memiliki masalah serupa di mana kelas memiliki variabel InputStream yang sebenarnya tidak harus saya pertahankan. Oleh karena itu mengubahnya menjadi Transient memecahkan masalah.
sumber
Setelah beberapa lama berjuang dengan masalah ini, saya yakin saya punya solusi. Masalahnya ada pada koneksi dua arah yang tidak terselesaikan, dan bagaimana merepresentasikan koneksi ketika mereka sedang dibuat serial. Cara untuk memperbaiki perilaku tersebut adalah dengan "memberi tahu"
gson
cara membuat serial objek. Untuk tujuan itu kami gunakanAdapters
.Dengan menggunakan
Adapters
kami dapat mengetahuigson
cara membuat serial setiap properti dariEntity
kelas Anda serta properti mana yang akan diserialkan.Membiarkan
Foo
danBar
menjadi dua entitas yangFoo
memilikiOneToMany
hubunganBar
danBar
memilikiManyToOne
hubunganFoo
. Kami mendefinisikanBar
adaptor sehingga ketika membuatgson
serialBar
, dengan menentukan bagaimana membuat serialFoo
dari perspektifBar
referensi siklik tidak akan mungkin.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; } }
Di sini
foo_id
digunakan untuk mewakiliFoo
entitas yang akan diserialisasi dan yang akan menyebabkan masalah referensi siklik. Nah bila kita menggunakan adapterFoo
tidak akan diserialisasi lagi dariBar
hanya id nya saja yang akan diambil dan dimasukkanJSON
. Sekarang kami memilikiBar
adaptor dan kami dapat menggunakannya untuk membuat serialFoo
. Inilah idenya: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; }
Satu hal lagi yang perlu diperjelas,
foo_id
tidak wajib dan dapat dilewati. Tujuan adaptor dalam contoh ini adalah untuk membuat serialBar
dan dengan menempatkanfoo_id
kami menunjukkan bahwaBar
dapat memicuManyToOne
tanpa menyebabkanFoo
memicuOneToMany
lagi ...Jawaban berdasarkan pengalaman pribadi, oleh karena itu silakan berkomentar, membuktikan saya salah, memperbaiki kesalahan, atau memperluas jawaban. Bagaimanapun saya berharap seseorang akan menemukan jawaban ini berguna.
sumber