Как решить циклическую ссылку в JSON сериализатор, вызванной двусторонней сопоставления библиотеки Hibernate?



Я пишу сериализатор для сериализации POJO в JSON, но застрял в проблеме круговой ссылки. В hibernate двунаправленное отношение один ко многим, родительские ссылки дочерние и дочерние ссылки обратно к родителю и здесь мой сериализатор умирает. (смотрите пример кода ниже)

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

998   12  

12 ответов:

может ли двунаправленная связь быть даже представлена в JSON? Некоторые форматы данных не подходят для некоторых типов моделирования данных.

один из методов работы с циклами при работе с графами объектов-это отслеживать, какие объекты вы видели до сих пор (используя сравнения идентичности), чтобы предотвратить переход по бесконечному циклу.

Я опираюсь на Google JSON для решения такого рода проблемы с помощью функции

исключение полей из сериализации и десериализации

предположим, что двунаправленная связь между классами A и B выглядит следующим образом

public class A implements Serializable {

    private B b;

}

Б

public class B implements Serializable {

    private A a;

}

теперь используйте GsonBuilder, чтобы получить пользовательский объект Gson следующим образом (Обратите внимание setExclusionStrategies метод)

Gson gson = new GsonBuilder()
    .setExclusionStrategies(new ExclusionStrategy() {

        public boolean shouldSkipClass(Class<?> clazz) {
            return (clazz == B.class);
        }

        /**
          * Custom field exclusion goes here
          */
        public boolean shouldSkipField(FieldAttributes f) {
            return false;
        }

     })
    /**
      * Use serializeNulls method if you want To serialize null values 
      * By default, Gson does not serialize null values
      */
    .serializeNulls()
    .create();

теперь наша круговая ссылка

A a = new A();
B b = new B();

a.setB(b);
b.setA(a);

String json = gson.toJson(a);
System.out.println(json);

посмотри GsonBuilder класс

Джексон 1.6 (выпущен в сентябре 2010 года) имеет специальную поддержку аннотаций для обработки такой связи родитель / потомок, см. http://wiki.fasterxml.com/JacksonFeatureBiDirReferences. (Wayback Snapshot)

вы можете, конечно, уже исключить сериализацию родительской ссылки, уже используя большинство пакетов обработки JSON (jackson, gson и flex-json по крайней мере поддерживают его), но реальный трюк заключается в том, как десериализовать его обратно (воссоздать Родительский ссылка), а не просто обрабатывать сторону сериализации. Хотя звучит, как сейчас просто исключение может работать для вас.

EDIT (апрель 2012):Джексон 2.0 теперь поддерживает true ссылки (Wayback Snapshot), Так что вы можете решить это так же.

при решении этой проблемы я использовал следующий подход (стандартизация процесса в моем приложении, что делает код понятным и многоразовым):

  1. создайте класс аннотаций для использования в полях, которые вы хотите исключить
  2. определите класс, который реализует интерфейс Google ExclusionStrategy
  3. создайте простой метод для создания объекта GSON с помощью GsonBuilder (аналогично объяснению Артура)
  4. комментировать поля, которые будут исключены по мере необходимости
  5. примените правила сериализации к вашему com.гуглить.гсон.Объект Gson
  6. сериализация объекта

вот код:

1)

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface GsonExclude {

}

2)

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;

public class GsonExclusionStrategy implements ExclusionStrategy{

    private final Class<?> typeToExclude;

    public GsonExclusionStrategy(Class<?> clazz){
        this.typeToExclude = clazz;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return ( this.typeToExclude != null && this.typeToExclude == clazz )
                    || clazz.getAnnotation(GsonExclude.class) != null;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(GsonExclude.class) != null;
    }

}

3)

static Gson createGsonFromBuilder( ExclusionStrategy exs ){
    GsonBuilder gsonbuilder = new GsonBuilder();
    gsonbuilder.setExclusionStrategies(exs);
    return gsonbuilder.serializeNulls().create();
}

4)

public class MyObjectToBeSerialized implements Serializable{

    private static final long serialVersionID = 123L;

    Integer serializeThis;
    String serializeThisToo;
    Date optionalSerialize;

    @GsonExclude
    @ManyToOne(fetch=FetchType.LAZY, optional=false)
    @JoinColumn(name="refobj_id", insertable=false, updatable=false, nullable=false)
    private MyObjectThatGetsCircular dontSerializeMe;

    ...GETTERS AND SETTERS...
}

5)

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

Gson gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(null) );
Gson _gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(Date.class) );

6)

MyObjectToBeSerialized _myobject = someMethodThatGetsMyObject();
String jsonRepresentation = gsonObj.toJson(_myobject);

или, чтобы исключить объект Date

String jsonRepresentation = _gsonObj.toJson(_myobject);

Если вы используете Jackon для сериализации, просто примените @JsonBackReference в свой Би-directinal сопоставление Это позволит решить проблему циклических ссылок.

Примечание: @JsonBackReference используется для решения бесконечной рекурсии (StackOverflowError)

использовал решение, похожее на Артура, но вместо setExclusionStrategies Я

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

и использовать @Expose аннотация gson для полей, которые мне нужны в json, другие поля исключены.

если вы используете Javascript, есть очень простое решение для этого с помощью

вот как я, наконец, решил это в моем случае. Это работает, по крайней мере, с Gson & Jackson.

private static final Gson gson = buildGson();

private static Gson buildGson() {
    return new GsonBuilder().addSerializationExclusionStrategy( getExclusionStrategy() ).create();  
}

private static ExclusionStrategy getExclusionStrategy() {
    ExclusionStrategy exlStrategy = new ExclusionStrategy() {
        @Override
        public boolean shouldSkipField(FieldAttributes fas) {
            return ( null != fas.getAnnotation(ManyToOne.class) );
        }
        @Override
        public boolean shouldSkipClass(Class<?> classO) {
            return ( null != classO.getAnnotation(ManyToOne.class) );
        }
    };
    return exlStrategy;
} 

эта ошибка может возникнуть, когда у вас есть два объекта:

class object1{
    private object2 o2;
}

class object2{
    private object1 o1;
}

С помощью GSon для сериализации, я получил эту ошибку:

java.lang.IllegalStateException: circular reference error

Offending field: o1

чтобы решить эту проблему, просто добавьте ключевое слово transient:

class object1{
    private object2 o2;
}

class object2{
    transient private object1 o1;
}

как вы можете увидеть здесь : почему Java имеет переходные поля?

ключевое слово transient в Java используется для указания того, что поле не должно быть сериализовано.

Джексон предоставляет JsonIdentityInfo аннотация для предотвращения циклических ссылок. Вы можете проверить учебник здесь.

номер ответа 8 Лучше, я думаю, что если вы знаете, какое поле выбрасывает ошибку, вы только устанавливаете fild в null и решаете.

List<RequestMessage> requestMessages = lazyLoadPaginated(first, pageSize, sortField, sortOrder, filters, joinWith);
    for (RequestMessage requestMessage : requestMessages) {
        Hibernate.initialize(requestMessage.getService());
        Hibernate.initialize(requestMessage.getService().getGroupService());
        Hibernate.initialize(requestMessage.getRequestMessageProfessionals());
        for (RequestMessageProfessional rmp : requestMessage.getRequestMessageProfessionals()) {
            Hibernate.initialize(rmp.getProfessional());
            rmp.setRequestMessage(null); // **
        }
    }

чтобы сделать код читаемым большой комментарий перенесен из комментария // ** ниже.

java.ленг.StackOverflowError [обработка запроса не удалось; вложенные исключением является org.springframework.http.конвертер.HttpMessageNotWritableException: не удалось написать JSON: бесконечная рекурсия (StackOverflowError) (через цепочку ссылок: com.service.pegazo.bo.RequestMessageProfessional["requestMessage"]->com.service.pegazo.bo.RequestMessage["requestMessageProfessionals"]

например, ProductBean имеет serialBean. Отображение будет двунаправленным отношением. Если мы сейчас попробуем использовать gson.toJson(), Он будет в конечном итоге с циклической ссылкой. Чтобы избежать этой проблемы, вы можете выполнить следующие действия:

  1. получить результаты из источника данных.
  2. повторите список и убедитесь, что serialBean не является null, а затем
  3. Set productBean.serialBean.productBean = null;
  4. тогда попробуйте использовать gson.toJson();

что должен решите проблему

Comments

    Ничего не найдено.