Chciałbym wygenerować ciąg JSON z mojego obiektu:
Gson gson = new Gson();
String json = gson.toJson(item);
Za każdym razem, gdy próbuję to zrobić, pojawia się ten błąd:
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)
Oto atrybuty mojej klasy 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;
Atrybuty mojej klasy BomModule, do której się odwołujemy :
private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
Masz jakiś pomysł, co powoduje ten błąd? Jak mogę to naprawić?
JsonWriter.java:473)
, w jaki sposób zidentyfikować przyczynę tej stackoverflow GsonOdpowiedzi:
Problem polega na tym, że masz odwołanie cykliczne.
W
BomModule
klasie, do której się odwołujesz:private Collection<BomModule> parentModules; private Collection<BomModule> subModules;
To odniesienie do siebie
BomModule
, oczywiście, wcale nie lubiane przez GSON.Aby obejść ten problem, wystarczy ustawić moduły,
null
aby uniknąć cyklicznego zapętlenia. W ten sposób mogę uniknąć wyjątku StackOverFlow-Exception.item.setModules(null);
Lub zaznacz pola, których nie chcesz wyświetlać w zserializowanym pliku JSON, używając
transient
słowa kluczowego, np .:private transient Collection<BomModule> parentModules; private transient Collection<BomModule> subModules;
źródło
Miałem ten problem, gdy miałem logger Log4J jako właściwość klasy, na przykład:
private Logger logger = Logger.getLogger(Foo.class);
Można to rozwiązać, wykonując rejestrator
static
lub po prostu przenosząc go do rzeczywistych funkcji.źródło
Jeśli używasz Realm i otrzymujesz ten błąd, a problem powodujący problem rozszerza RealmObject, nie zapomnij zrobić
realm.copyFromRealm(myObject)
kopii bez wszystkich powiązań Realm przed przejściem do GSON w celu serializacji.Brakowało mi zrobienia tego, ponieważ tylko jeden z wielu kopiowanych obiektów ... zajęło mi to wieki, ponieważ ślad stosu nie nazywa klasy / typu obiektu. Chodzi o to, że problem jest spowodowany przez odwołanie cykliczne, ale jest to odwołanie cykliczne gdzieś w klasie bazowej RealmObject, a nie twoja własna podklasa, co utrudnia wykrycie!
źródło
Jak powiedział SLaks, StackOverflowError się zdarza, jeśli masz cykliczne odwołanie w swoim obiekcie.
Aby to naprawić, możesz użyć TypeAdapter dla swojego obiektu.
Na przykład, jeśli potrzebujesz tylko wygenerować String z obiektu, możesz użyć adaptera w następujący sposób:
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()); } }
i zarejestruj to w ten sposób:
Gson gson = new GsonBuilder() .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>()) .create();
lub w ten sposób, jeśli masz interfejs i chcesz używać adaptera dla wszystkich jego podklas:
Gson gson = new GsonBuilder() .registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>()) .create();
źródło
Moja odpowiedź jest trochę spóźniona, ale myślę, że to pytanie nie ma jeszcze dobrego rozwiązania. Znalazłem to pierwotnie tutaj .
Za pomocą Gson możesz oznaczyć pola, które chcesz uwzględnić w json, w
@Expose
ten sposób:@Expose String myString; // will be serialized as myString
i utwórz obiekt gson z:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Okrągłe odniesienia, których po prostu nie ujawniasz. To mi wystarczyło!
źródło
Ten błąd występuje często, gdy masz rejestrator w swojej superklasie. Jak zasugerował @Zar wcześniej, możesz użyć statycznego dla swojego pola rejestratora, ale to również działa:
protected final transient Logger logger = Logger.getLogger(this.getClass());
PS prawdopodobnie będzie działać iz adnotacją @Expose sprawdź więcej na ten temat tutaj: https://stackoverflow.com/a/7811253/1766166
źródło
Mam ten sam problem. W moim przypadku powodem było to, że konstruktor mojej serializowanej klasy pobierał zmienną kontekstową, taką jak ta:
public MetaInfo(Context context)
Kiedy usuwam ten argument, błąd zniknął.
public MetaInfo()
źródło
Edycja: Przepraszam za mój błąd, to moja pierwsza odpowiedź. Dziękuję za rady.
Tworzę własny konwerter Json
Głównym rozwiązaniem, którego użyłem, jest utworzenie zestawu obiektów nadrzędnych dla każdego odniesienia do obiektu. Jeśli odniesienie podrzędne wskazuje na istniejący obiekt nadrzędny, zostanie odrzucone. Następnie łączę się z dodatkowym rozwiązaniem, ograniczając czas odniesienia, aby uniknąć nieskończonej pętli w dwukierunkowej relacji między bytami.
Mój opis nie jest zbyt dobry, mam nadzieję, że wam pomoże.
To mój pierwszy wkład w społeczność Java (rozwiązanie Twojego problemu). Możecie to sprawdzić;) Jest plik README.md https://github.com/trannamtrung1st/TSON
źródło
W Androidzie przepełnienie stosu gson okazało się deklaracją Handlera. Przeniesiono go do klasy, która nie jest deserializowana.
Opierając się na zaleceniu Zara, uczyniłem handler statycznym, gdy stało się to w innej sekcji kodu. Uruchamianie programu obsługi również działało.
źródło
BomItem
odnosi się doBOMModule
(Collection<BomModule> modules
) iBOMModule
odnosi się doBOMItem
(Collection<BomItem> items
). Biblioteka Gson nie lubi odwołań cyklicznych. Usuń tę zależność cykliczną ze swojej klasy. Ja również miałem w przeszłości ten sam problem z gson lib.źródło
Miałem ten problem, który wystąpił u mnie, gdy wstawiłem:
Logger logger = Logger.getLogger( this.getClass().getName() );
w moim obiekcie ... co miało sens po mniej więcej godzinie debugowania!
źródło
W przypadku użytkowników systemu Android nie można serializować pliku
Bundle
ze względu na odwołanie do samego siebieBundle
powodujące utworzenie plikuStackOverflowError
.Aby serializować pakiet, zarejestruj plik
BundleTypeAdapterFactory
.źródło
Unikaj niepotrzebnych obejść, takich jak ustawianie wartości na null lub zmienianie pól jako przejściowe. Właściwym sposobem na to jest dodanie adnotacji do jednego z pól za pomocą @Expose, a następnie polecenie Gson, aby serializował tylko pola z adnotacją:
private Collection<BomModule> parentModules; @Expose private Collection<BomModule> subModules; ... Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
źródło
Miałem podobny problem, w którym klasa miała zmienną InputStream, której tak naprawdę nie musiałem utrwalać. Dlatego zmiana go na Przejściowa rozwiązała problem.
źródło
Po pewnym czasie walcząc z tym problemem, wydaje mi się, że mam rozwiązanie. Problem dotyczy nierozwiązanych połączeń dwukierunkowych i sposobu reprezentowania połączeń podczas ich serializacji. Sposobem na naprawienie tego zachowania jest „poinformowanie”,
gson
jak serializować obiekty. W tym celu używamyAdapters
.Używając
Adapters
, możemy powiedzieć,gson
jak serializować każdą właściwość z plikuEntity
klasy, a także które właściwości serializować.Niech
Foo
iBar
będą dwoma podmiotami, w którychFoo
maOneToMany
związekBar
iBar
maManyToOne
związekFoo
. DefiniujemyBar
adapter tak, aby podczasgson
serializacjiBar
zdefiniowanie sposobu serializacjiFoo
z perspektywyBar
cyklicznego odwoływania się nie było możliwe.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; } }
Tutaj
foo_id
jest używany do reprezentowaniaFoo
obiektu, który zostałby serializowany i który spowodowałby nasz problem z cyklicznymi odwołaniami. Teraz, gdy używamy adapteraFoo
nie będzie ponownie serializowany,Bar
tylko jego identyfikator zostanie pobrany i wstawionyJSON
. Teraz mamyBar
adapter i możemy go użyć do serializacjiFoo
. Oto pomysł: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; }
Jeszcze jedna rzecz do wyjaśnienia,
foo_id
nie jest obowiązkowa i można ją pominąć. Celem adaptera w tym przykładzie jest serializacjaBar
i poprzez umieszczeniefoo_id
pokazaliśmy, żeBar
może wyzwalaćManyToOne
bez powodowaniaFoo
wyzwalaniaOneToMany
ponownego ...Odpowiedź opiera się na osobistych doświadczeniach, dlatego zachęcam do komentowania, udowodnienia, że się mylę, poprawienia błędów lub rozszerzenia odpowiedzi. W każdym razie mam nadzieję, że ta odpowiedź komuś się przyda.
źródło