Gson.toJson() выбрасывает StackOverflowError

Я хотел бы сгенерировать строку 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;

Любая идея, что вызывает эту ошибку? Как я могу это исправить?

Ответ 1

Это может произойти, если у вас есть круговая ссылка.

Указывает ли BomModule ваш родительский объект?

Ответ 2

У меня была эта проблема, когда у меня был логгер Log4J как свойство класса, например:

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

Это можно решить, либо сделав logger static, либо просто переместив его в действительную функцию (ы).

Ответ 3

Как сказал Слэкс, StackOverflowError произойдет, если у вас есть круговая ссылка в вашем объекте.

Чтобы исправить это, вы можете использовать TypeAdapter для своего объекта.

Например, если вам нужно только создать String из вашего объекта, вы можете использовать адаптер следующим образом:

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

и зарегистрируйте его следующим образом:

Gson gson = new GsonBuilder()
               .registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
               .create();

или как это, если у вас есть интерфейс и вы хотите использовать адаптер для всех его подклассов:

Gson gson = new GsonBuilder()
               .registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>())
               .create();

Ответ 4

Если вы используете Realm, и вы получите эту ошибку, и объект, предоставляющий проблемы, расширяет RealmObject, не забудьте сделать realm.copyFromRealm(myObject), чтобы создать копию без привязки Realm до перехода к GSON для сериализации.

Я бы пропустил это для всего лишь одного из множества копируемых объектов... взял меня на себя, чтобы понять, как трассировка стека не называет класс объекта/тип. Дело в том, что проблема вызвана циркулярной ссылкой, но она является круглой ссылкой где-то в базовом классе RealmObject, а не в вашем собственном подклассе, что затрудняет выявление!

Ответ 5

Эта ошибка распространена, когда у вас есть журнал в вашем суперклассе. Как и предлагал @Zar, вы можете использовать статический для поля вашего журнала, но это также работает:

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

P.S. вероятно, это сработает, и с аннотацией @Expose проверьте подробнее об этом здесь: fooobar.com/questions/20692/...

Ответ 6

У меня та же проблема. В моем случае причина заключалась в том, что конструктор моего сериализованного класса принимает переменную контекста, например:

public MetaInfo(Context context)

Когда я удалю этот аргумент, ошибка исчезла.

public MetaInfo()

Ответ 7

В Android переполнение стека gson оказалось объявлением обработчика. Переместил его в класс, который не десериализован.

Основываясь на рекомендации Zar, я заставил обработчик статическим, когда это произошло в другом разделе кода. Создание статического обработчика также работало.

Ответ 8

BomItem относится к BOMModule (Collection<BomModule> modules), а BOMModule относится к BomItem (Collection<BomItem> items). Библиотека Gson не любит круговые ссылки. Удалите эту циклическую зависимость от вашего класса. Я тоже сталкивался с такой же проблемой в прошлом с gson lib.

Ответ 9

Мой ответ немного запоздал, но я думаю, что у этого вопроса пока нет хорошего решения. Я нашел его изначально здесь.

С помощью Gson вы можете пометить поля, в которых вы do хотите включить в json с @Expose, и создать объект gson с помощью:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

Это помогло!