Je souhaite générer une chaîne JSON à partir de mon objet:
Gson gson = new Gson();
String json = gson.toJson(item);
Chaque fois que j'essaie de faire cela, j'obtiens cette erreur:
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)
Voici les attributs de ma classe 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;
Attributs de ma classe BomModule référencée :
private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
Une idée de ce qui cause cette erreur? Comment puis-je y remédier?
JsonWriter.java:473)
, comment identifier la cause racine du stackoverflow GsonRéponses:
Ce problème est que vous avez une référence circulaire.
Dans le cours
BomModule
auquel vous faites référence:private Collection<BomModule> parentModules; private Collection<BomModule> subModules;
Cette référence à soi-même
BomModule
n'est évidemment pas du tout appréciée par GSON.Une solution de contournement consiste simplement à définir les modules sur
null
pour éviter la boucle récursive. De cette façon, je peux éviter l'exception StackOverFlow.item.setModules(null);
Ou marquez les champs que vous ne voulez pas afficher dans le json sérialisé en utilisant le
transient
mot - clé, par exemple:private transient Collection<BomModule> parentModules; private transient Collection<BomModule> subModules;
la source
J'ai eu ce problème lorsque j'avais un enregistreur Log4J comme propriété de classe, tel que:
private Logger logger = Logger.getLogger(Foo.class);
Cela peut être résolu en fabriquant l'enregistreur
static
ou simplement en le déplaçant dans la ou les fonctions réelles.la source
Si vous utilisez Realm et que vous obtenez cette erreur et que l'objet qui pose problème étend RealmObject, n'oubliez pas
realm.copyFromRealm(myObject)
de créer une copie sans toutes les liaisons de Realm avant de passer à GSON pour la sérialisation.J'avais manqué de faire cela pour un seul parmi un tas d'objets copiés ... m'a pris du temps à réaliser car la trace de la pile ne nomme pas la classe / le type d'objet. Le problème est que le problème est causé par une référence circulaire, mais c'est une référence circulaire quelque part dans la classe de base RealmObject, pas votre propre sous-classe, ce qui la rend plus difficile à repérer!
la source
Comme SLaks l'a dit, StackOverflowError se produit si vous avez une référence circulaire dans votre objet.
Pour résoudre ce problème, vous pouvez utiliser TypeAdapter pour votre objet.
Par exemple, si vous n'avez besoin que de générer une chaîne à partir de votre objet, vous pouvez utiliser un adaptateur comme celui-ci:
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()); } }
et enregistrez-le comme ceci:
Gson gson = new GsonBuilder() .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>()) .create();
ou comme ceci, si vous avez une interface et que vous souhaitez utiliser l'adaptateur pour toutes ses sous-classes:
Gson gson = new GsonBuilder() .registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>()) .create();
la source
Ma réponse est un peu tardive, mais je pense que cette question n'a pas encore de bonne solution. Je l'ai trouvé à l'origine ici .
Avec Gson vous pouvez marquer les champs que vous ne souhaitez inclure dans JSON avec
@Expose
comme celui - ci:@Expose String myString; // will be serialized as myString
et créez l'objet gson avec:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Des références circulaires que vous n'exposez tout simplement pas . Cela a fait l'affaire pour moi!
la source
Cette erreur est courante lorsque vous avez un enregistreur dans votre super classe. Comme @Zar l'a suggéré auparavant, vous pouvez utiliser statique pour votre champ de journalisation, mais cela fonctionne également:
protected final transient Logger logger = Logger.getLogger(this.getClass());
PS cela fonctionnera probablement et avec l'annotation @Expose, vérifiez plus à ce sujet ici: https://stackoverflow.com/a/7811253/1766166
la source
J'ai le même problème. Dans mon cas, la raison était que le constructeur de ma classe sérialisée prend une variable de contexte, comme ceci:
public MetaInfo(Context context)
Lorsque je supprime cet argument, l'erreur a disparu.
public MetaInfo()
la source
Edit: Désolé pour mon mal, c'est ma première réponse. Merci pour vos conseils.
Je crée mon propre convertisseur Json
La principale solution que j'ai utilisée est de créer un ensemble d'objets parents pour chaque référence d'objet. Si une sous-référence pointe vers un objet parent existant, elle sera supprimée. Ensuite, je combine avec une solution supplémentaire, en limitant le temps de référence pour éviter une boucle infinitive en relation bidirectionnelle entre entités.
Ma description n'est pas trop bonne, j'espère que cela vous aidera les gars.
C'est ma première contribution à la communauté Java (solution à votre problème). Vous pouvez le vérifier;) Il existe un fichier README.md https://github.com/trannamtrung1st/TSON
la source
Dans Android, le débordement de pile gson s'est avéré être la déclaration d'un gestionnaire. Je l'ai déplacé vers une classe qui n'est pas désérialisée.
Sur la base de la recommandation de Zar, j'ai rendu le gestionnaire statique lorsque cela se produisait dans une autre section de code. Rendre le gestionnaire statique fonctionnait également.
la source
BomItem
fait référence àBOMModule
(Collection<BomModule> modules
) etBOMModule
fait référence àBOMItem
(Collection<BomItem> items
). La bibliothèque Gson n'aime pas les références circulaires. Supprimez cette dépendance circulaire de votre classe. J'avais moi aussi été confronté au même problème dans le passé avec gson lib.la source
J'ai eu ce problème pour moi quand j'ai mis:
Logger logger = Logger.getLogger( this.getClass().getName() );
dans mon objet ... ce qui était parfaitement logique après environ une heure de débogage!
la source
Pour les utilisateurs d'Android, vous ne pouvez pas sérialiser un en
Bundle
raison d'une auto-référence àBundle
l'origine d'unStackOverflowError
.Pour sérialiser un bundle, enregistrez un fichier
BundleTypeAdapterFactory
.la source
Évitez les solutions de contournement inutiles, telles que la définition de valeurs sur null ou la création de champs transitoires. La bonne façon de faire est d'annoter l'un des champs avec @Expose, puis de dire à Gson de ne sérialiser que les champs avec l'annotation:
private Collection<BomModule> parentModules; @Expose private Collection<BomModule> subModules; ... Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
la source
J'ai eu un problème similaire où la classe avait une variable InputStream que je n'avais pas vraiment à persister. Par conséquent, le changer en Transitoire a résolu le problème.
la source
Après un certain temps à lutter contre ce problème, je pense avoir une solution. Le problème concerne les connexions bidirectionnelles non résolues et la façon de représenter les connexions lorsqu'elles sont sérialisées. La manière de corriger ce comportement est de "dire"
gson
comment sérialiser des objets. À cette fin, nous utilisonsAdapters
.En utilisant,
Adapters
nous pouvons diregson
comment sérialiser chaque propriété de votreEntity
classe ainsi que les propriétés à sérialiser.Soit
Foo
etBar
soit deux entités oùFoo
a uneOneToMany
relationBar
etBar
a uneManyToOne
relation avecFoo
. Nous définissons l'Bar
adaptateur de sorte que lors de lagson
sérialisationBar
, en définissant comment sérialiserFoo
du point de vue duBar
référencement cyclique ne sera pas possible.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; } }
Ici
foo_id
est utilisé pour représenter l'Foo
entité qui serait sérialisée et qui causerait notre problème de référencement cyclique. Maintenant, lorsque nous utilisons l'adaptateur,Foo
il ne sera plus sérialisé à partir deBar
seulement son identifiant sera pris et inséréJSON
. Nous avons maintenant unBar
adaptateur et nous pouvons l'utiliser pour sérialiserFoo
. Voici une idée: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; }
Une dernière chose à clarifier,
foo_id
n'est pas obligatoire et peut être ignorée. Le but de l'adaptateur dans cet exemple est de sérialiserBar
et en mettantfoo_id
nous avons montré qu'ilBar
peut se déclencherManyToOne
sans provoquerFoo
de déclenchement àOneToMany
nouveau ...La réponse est basée sur l'expérience personnelle, n'hésitez donc pas à commenter, à me prouver le contraire, à corriger les erreurs ou à élargir la réponse. Quoi qu'il en soit, j'espère que quelqu'un trouvera cette réponse utile.
la source