Eu gostaria de gerar uma string JSON do meu objeto:
Gson gson = new Gson();
String json = gson.toJson(item);
Sempre que tento fazer isso, recebo este erro:
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)
Estes são os atributos da minha 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;
Atributos da minha classe BomModule referenciada :
private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
Alguma ideia do que causa esse erro? Como posso corrigir isso?
JsonWriter.java:473)
, como identificar a causa raiz do stackoverflow GsonRespostas:
Esse problema é que você tem uma referência circular.
Na
BomModule
aula a que você está se referindo:private Collection<BomModule> parentModules; private Collection<BomModule> subModules;
Essa auto-referência a
BomModule
, obviamente, não é apreciada por GSON.Uma solução alternativa é apenas definir os módulos
null
para evitar o loop recursivo. Dessa forma, posso evitar a exceção StackOverFlow.item.setModules(null);
Ou marque os campos que não deseja que apareçam no json serializado usando a
transient
palavra - chave, por exemplo:private transient Collection<BomModule> parentModules; private transient Collection<BomModule> subModules;
fonte
Tive esse problema quando tinha um registrador Log4J como uma propriedade de classe, como:
private Logger logger = Logger.getLogger(Foo.class);
Isso pode ser resolvido criando o logger
static
ou simplesmente movendo-o para as funções reais.fonte
Se você estiver usando o Realm e obtiver este erro, e o objeto que está causando o problema estende o RealmObject, não se esqueça
realm.copyFromRealm(myObject)
de criar uma cópia sem todas as ligações do Realm antes de passar para o GSON para serialização.Eu senti falta de fazer isso por apenas um entre um monte de objetos sendo copiados ... levei muito tempo para perceber, pois o rastreamento de pilha não nomeia a classe / tipo de objeto. O problema é que o problema é causado por uma referência circular, mas é uma referência circular em algum lugar da classe base RealmObject, não sua própria subclasse, o que torna mais difícil identificá-la!
fonte
Como SLaks disse StackOverflowError acontecerá se você tiver uma referência circular em seu objeto.
Para corrigir isso, você pode usar TypeAdapter para o seu objeto.
Por exemplo, se você precisa apenas gerar String a partir do seu objeto, você pode usar o adaptador como este:
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()); } }
e registre-o assim:
Gson gson = new GsonBuilder() .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>()) .create();
ou assim, se você tem interface e deseja usar o adaptador para todas as suas subclasses:
Gson gson = new GsonBuilder() .registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>()) .create();
fonte
Minha resposta está um pouco atrasada, mas acho que essa questão ainda não tem uma boa solução. Eu o encontrei originalmente aqui .
Com Gson você pode marcar os campos que não querem ser incluídos em JSON com
@Expose
como esta:@Expose String myString; // will be serialized as myString
e crie o objeto gson com:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Referências circulares que você simplesmente não expõe. Isso funcionou para mim!
fonte
Este erro é comum quando você tem um logger em sua superclasse. Como @Zar sugeriu antes, você pode usar estático para o campo do registrador, mas isso também funciona:
protected final transient Logger logger = Logger.getLogger(this.getClass());
PS provavelmente funcionará e com a anotação @Expose verifique mais sobre isso aqui: https://stackoverflow.com/a/7811253/1766166
fonte
Eu tenho o mesmo problema. No meu caso, o motivo foi que o construtor da minha classe serializada leva variável de contexto, como esta:
public MetaInfo(Context context)
Quando excluo esse argumento, o erro desaparece.
public MetaInfo()
fonte
Edit: Desculpe pelo meu erro, esta é minha primeira resposta. Obrigado por seus conselhos.
Eu crio meu próprio conversor Json
A principal solução que usei foi criar um conjunto de objetos pais para cada referência de objeto. Se uma sub-referência apontar para um objeto pai existente, ela será descartada. Então eu combino com uma solução extra, limitando o tempo de referência para evitar loop infinitivo no relacionamento bidirecional entre entidades.
Minha descrição não é muito boa, espero que ajude vocês.
Esta é minha primeira contribuição para a comunidade Java (solução para seu problema). Você pode verificar;) Há um arquivo README.md https://github.com/trannamtrung1st/TSON
fonte
No Android, gson stack overflow acabou sendo a declaração de um Handler. Movido para uma classe que não está sendo desserializada.
Com base na recomendação de Zar, tornei o manipulador estático quando isso aconteceu em outra seção do código. Deixar o manipulador estático também funcionou.
fonte
BomItem
refere-se aBOMModule
(Collection<BomModule> modules
) eBOMModule
refere-se aBOMItem
(Collection<BomItem> items
). A biblioteca Gson não gosta de referências circulares. Remova esta dependência circular de sua classe. Eu também havia enfrentado o mesmo problema no passado com o gson lib.fonte
Eu tive esse problema ocorrendo para mim quando coloquei:
Logger logger = Logger.getLogger( this.getClass().getName() );
no meu objeto ... o que fazia todo o sentido depois de uma hora ou mais de depuração!
fonte
Para usuários do Android, você não pode serializar um
Bundle
devido a uma auto-referência paraBundle
causar umStackOverflowError
.Para serializar um pacote, registre a
BundleTypeAdapterFactory
.fonte
Evite soluções alternativas desnecessárias, como definir valores como nulos ou tornar os campos temporários. A maneira certa de fazer isso é anotar um dos campos com @Expose e, em seguida, dizer ao Gson para serializar apenas os campos com a anotação:
private Collection<BomModule> parentModules; @Expose private Collection<BomModule> subModules; ... Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
fonte
Tive um problema semelhante em que a classe tinha uma variável InputStream que realmente não precisava persistir. Portanto, alterá-lo para Transiente resolveu o problema.
fonte
Depois de algum tempo lutando com esse problema, acredito ter uma solução. O problema está nas conexões bidirecionais não resolvidas e em como representar as conexões quando elas estão sendo serializadas. A maneira de corrigir esse comportamento é "dizer"
gson
como serializar objetos. Para esse efeito, usamosAdapters
.Ao usar
Adapters
, podemos dizergson
como serializar cada propriedade de suaEntity
classe, bem como quais propriedades serializar.Deixe
Foo
eBar
seja duas entidades ondeFoo
temOneToMany
relação comBar
eBar
temManyToOne
relação comFoo
. DefinimosBar
adaptador de forma que, aogson
serializarBar
, definir como serializarFoo
da perspectiva daBar
referência cíclica não será possível.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; } }
Aqui
foo_id
é usado para representar aFoo
entidade que seria serializada e que causaria nosso problema de referência cíclica. Agora, quando usarmos o adaptadorFoo
, não será serializado novamente,Bar
apenas seu id será obtido e inseridoJSON
. Agora temosBar
adaptador e podemos usá-lo para serializarFoo
. Aqui está a ideia: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; }
Mais uma coisa a esclarecer,
foo_id
não é obrigatório e pode ser ignorado. O objetivo do adaptador neste exemplo é serializarBar
e, colocandofoo_id
, mostramos queBar
pode dispararManyToOne
sem causar oFoo
disparoOneToMany
novamente ...A resposta é baseada na experiência pessoal, portanto, fique à vontade para comentar, provar que estou errado, consertar erros ou expandir a resposta. De qualquer forma, espero que alguém ache esta resposta útil.
fonte