[java] 최대 절전 양방향 매핑으로 인한 json serializer의 순환 참조를 해결하는 방법은 무엇입니까?

POJO를 JSON으로 직렬화하는 serializer를 작성 중이지만 순환 참조 문제에 갇혀 있습니다. 최대 절전 모드 양방향 일대 다 관계에서 부모는 자식 및 자식 참조를 부모로 다시 참조하고 여기서 내 직렬 변환기가 죽습니다. (아래 예제 코드 참조)
이주기를 끊는 방법은 무엇입니까? 개체의 소유자 트리를 가져와 개체 자체가 소유자 계층의 어딘가에 있는지 확인할 수 있습니까? 참조가 순환되는지 확인하는 다른 방법이 있습니까? 또는이 문제를 해결하기위한 다른 아이디어가 있습니까?



답변

양방향 관계를 JSON으로 표현할 수도 있습니까? 일부 데이터 형식은 일부 데이터 모델링 유형에 적합하지 않습니다.

순회 개체 그래프를 처리 할 때주기를 처리하는 한 가지 방법은 지금까지 본 개체를 추적하여 (ID 비교 사용) 무한주기를 통과하지 못하도록하는 것입니다.


답변

이 기능을 사용하여 이러한 종류의 문제를 처리하기 위해 Google JSON 에 의존 합니다.

직렬화 및 역 직렬화에서 필드 제외

다음과 같이 A와 B 클래스 사이의 양방향 관계를 가정하십시오.

public class A implements Serializable {

    private B b;

}

그리고 B

public class B implements Serializable {

    private A a;

}

이제 GsonBuilder를 사용하여 다음과 같이 사용자 지정 Gson 개체를 가져옵니다 (Notice 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 클래스 살펴보기


답변

Jackson 1.6 (2010 년 9 월 출시)에는 이러한 부모 / 자식 연결을 처리하기위한 특정 주석 기반 지원이 있습니다. http://wiki.fasterxml.com/JacksonFeatureBiDirReferences를 참조 하십시오 . ( 웨이 백 스냅 샷 )

물론 이미 대부분의 JSON 처리 패키지 (jackson, gson 및 flex-json이 지원)를 이미 사용하고있는 상위 링크의 직렬화를 제외 할 수 있지만 실제 트릭은 다시 직렬화 (상위 링크 다시 생성)하는 방법이 아닙니다. 직렬화 측면을 처리하십시오. 지금은 제외가 효과가있을 수 있습니다.

편집 (2012 년 4 월) : Jackson 2.0은 이제 진정한 ID 참조 ( Wayback Snapshot )를 지원 하므로이 방법으로도 해결할 수 있습니다.


답변

이 문제를 해결하기 위해 다음과 같은 접근 방식을 취했습니다 (애플리케이션 전체에서 프로세스를 표준화하여 코드를 명확하고 재사용 가능하게 함).

  1. 제외하려는 필드에 사용할 주석 클래스를 만듭니다.
  2. Google의 ExclusionStrategy 인터페이스를 구현하는 클래스 정의
  3. GsonBuilder를 사용하여 GSON 객체를 생성하는 간단한 방법을 만듭니다 (Arthur의 설명과 유사).
  4. 필요에 따라 제외 할 필드에 주석을 답니다.
  5. com.google.gson.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;
    }

}

삼)

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 를 양방향 매핑에 적용 하면 순환 참조 문제가 해결됩니다.

참고 : @JsonBackReference는 무한 재귀 (StackOverflowError)를 해결하는 데 사용됩니다.


답변

대신 아서의,하지만 비슷한 솔루션을 사용 setExclusionStrategies내가 사용

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

@Exposejson에서 필요한 필드에 gson 주석을 사용 하면 다른 필드는 제외됩니다.


답변

스프링 부트를 사용하는 경우 Jackson은 순환 / 양방향 데이터에서 응답을 생성하는 동안 오류가 발생하므로

안녕하세요.

순환 성을 무시하다

At Parent:
@OneToMany(mappedBy="dbApp")
@JsonIgnoreProperties("dbApp")
private Set<DBQuery> queries;

At child:
@ManyToOne
@JoinColumn(name = "db_app_id")
@JsonIgnoreProperties("queries")
private DBApp dbApp;