Вопрос по java, json – gson.toJson () генерирует StackOverflowError

65

Я хотел бы создать строку JSON из моего объекта:

<code>Gson gson = new Gson();
String json = gson.toJson(item);
</code>

Каждый раз, когда я пытаюсь сделать это, я получаю эту ошибку:

<code>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)
</code>

Это атрибуты моегоBomItem учебный класс:

<code>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;
</code>

Атрибуты моих ссылокBomModule учебный класс:

<code>private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
</code>

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

Может случиться, если вы поместите экземпляр объекта внутрь себя где-то внутри gson. Christophe Roussy
Исключение теряет основную причину и запускает журнал сJsonWriter.java:473) Как определить причину переполнения стека Gson Siddharth

Ваш Ответ

13   ответов
15

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

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

Используя царство, это было именно то решение, которое решает проблему, спасибо Jude Fernandes
Правильно! В моем случае измените мой список объектов, запрашиваемый непосредственно из области, на ArrayList <Image> copyList = new ArrayList <> (); for (изображение: изображения) {copyList.add (realm.copyFromRealm (изображение)); } Ricardo Mutti
24

когда я использовал Log4J logger в качестве свойства класса, например:

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

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

Другой способ решить эту проблему - добавить модификатор переходного процесса в поле gawi
logger в основном должен быть статичным. В противном случае вы будете нести затраты на получение этого экземпляра Logger для каждого объекта, что, вероятно, не то, что вам нужно. (Стоимость не тривиальная) stolsvik
Абсолютно отличный улов. Эта самостоятельная ссылка на класс явно не понравилась GSON. Спасло меня много головных болей! + 1 christopher
62

ВBomModule класс, на который вы ссылаетесь:

private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;

Это самостоятельная ссылка наBomModule, очевидно, вообще не понравилась GSON.

Обходной путь - просто установите модули вnull чтобы избежать рекурсивного зацикливания. Таким образом, я могу избежать исключения StackOverFlow-Exception.

item.setModules(null);

Или пометьте поля, которые вы не хочу, чтобы показать в сериализованном JSON с помощьюtransient ключевое слово, например:

private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;
Да, объект BomModule может быть частью другого объекта BomModule. nimrod
Но это проблема? 'Collection <BomModule> modules' - это всего лишь коллекция, и я думаю, что gson сможет создать из нее простой массив? nimrod
@ dooonot: какой-нибудь из объектов в коллекции ссылается на родительский объект? SLaks
Я не уверен, правильно ли я тебя понял, но да. Пожалуйста, смотрите обновленный вопрос выше. nimrod
@ dooonot: как я и подозревал, при сериализации родительской и дочерней коллекций происходит бесконечный цикл. Какой JSON вы ожидаете написать? SLaks
5

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

С Гсоном вы можете пометить поля, которые выделат хочу быть включенным в JSON с@Expose так

@Expose
String myString;  // will be serialized as myString

и создайте объект gson с помощью:

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

Круговые ссылки вы простон разоблачить. Это помогло мне!

Знаете ли вы, есть ли аннотация, которая противоположна этому? Есть 4 поля, которые мне нужно игнорировать, и более 30, которые мне нужно включить. jDub9
@ jDub9 Извините за поздний ответ, но я был в отпуске. Посмотри наэт ответ. Надеюсь, это решит вашу проблем ffonz
1

Извините за мой плохой, это мой первый ответ. Спасибо за ваши советы.

Я создаю свой собственный Json Converter

Основное решение, которое я использовал, - это создание родительского объекта для каждой ссылки на объект. Если подссылка указывает на существующий родительский объект, она будет отброшена. Затем я объединяюсь с дополнительным решением, ограничивающим эталонное время, чтобы избежать бесконечного цикла в двунаправленных отношениях между сущностями.

Мое описание не слишком хорошее, надеюсь, оно поможет вам, ребята.

Это мой первый вклад в сообщество Java (решение вашей проблемы). Вы можете проверить это;) Есть файл README.mdhttps: //github.com/trannamtrung1st/TSO

Ссылка на решение приветствуется, но убедитесь, что ваш ответ без нее полезен: добавить контекст вокруг ссылки так что ваши коллеги-пользователи будут иметь некоторое представление о том, что это такое и зачем оно там, а затем процитировать наиболее релевантную часть страницы, на которую вы ссылаетесь, в случае, если целевая страница недоступна. Ответы, которые немного больше, чем ссылка, могут быть удалены. Paul Roub
Подобно тому, что говорили другие комментаторы, предпочтительно, чтобы вы отображали наиболее важные части вашего кода в своем посте. Кроме того, вам не нужно извиняться за ошибки в вашем ответе. LAD
Спасибо. Я должен был отредактировать мой ответ. Надеюсь, что все будет хорошо: D Trần Nam Trung
Самостоятельное продвижение Просто ссылка на вашу собственную библиотеку или учебник не является хорошим ответом. Ссылка на него, объяснение, почему это решает проблему, предоставление кода о том, как это сделать и отказ от написанного вами, делает для лучшего ответа. Видеть: Что означает «хорошая» самореклама? Shree
11

StackOverflowError произойдет, если в вашем объекте есть циклическая ссылка.

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

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

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

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

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

P.S. вероятно, это будет работать, и с аннотацией @Expose проверьте больше об этом здесь:https: //stackoverflow.com/a/7811253/176616

1

что конструктор моего сериализованного класса принимает переменную контекста, например так:

public MetaInfo(Context context)

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

public MetaInfo()
@ user802467 Вы имеете в виду сервис Android? Preetam
Я столкнулся с этой проблемой при передаче ссылки на объект службы в качестве контекста. Исправлено было сделать статическую переменную контекста в классе, который использует gson.toJson (this). user802467
0

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

0

Logger logger = Logger.getLogger( this.getClass().getName() );

в моем объекте ... который имел смысл после часа отладки!

0

таких как установка значений на ноль или создание переходных полей. Правильный способ сделать это - аннотировать одно из полей с помощью @Expose, а затем указать Gson сериализовать только поля с аннотацией:

private Collection<BomModule> parentModules;
@Expose
private Collection<BomModule> subModules;

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

еремещено в класс, который не десериализован.

По рекомендации Зара я сделал статический обработчик, когда это произошло в другом разделе кода. Создание статического обработчика также сработало.

0

Bundle из-за самостоятельной ссылки наBundle вызываяStackOverflowError.

Для сериализации пакета, зарегистрироватьBundleTypeAdapterFactory.

Похожие вопросы