如何解决由hibernate双向映射引起的json序列化器中的循环引用?

WSK*_*WSK 75 java serialization json hibernate

我正在编写一个序列化程序来将POJO序列化为JSON,但却陷入循环引用问题.在hibernate双向一对多关系中,父引用子和子引用回父,这里我的序列化器死了.(参见下面的示例代码)
如何打破这个循环?我们可以获取对象的所有者树,以查看对象本身是否存在于其自己的所有者层次结构中的某个位置?任何其他方式来查找引用是否将是循环的?或任何其他想法来解决这个问题?

Art*_*ald 45

我依靠Google JSON通过使用该功能来处理此类问题

从序列化和反序列化中排除字段

假设A和B类之间的双向关系如下

public class A implements Serializable {

    private B b;

}
Run Code Online (Sandbox Code Playgroud)

而B

public class B implements Serializable {

    private A a;

}
Run Code Online (Sandbox Code Playgroud)

现在使用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();
Run Code Online (Sandbox Code Playgroud)

现在我们的循环参考

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

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

String json = gson.toJson(a);
System.out.println(json);
Run Code Online (Sandbox Code Playgroud)

看看GsonBuilder

  • 这通过删除它来"解决"循环引用.无法从生成的JSON重建原始数据结构. (8认同)

Sta*_*Man 33

Jackson 1.6(2010年9月发布)具有特定的基于注释的支持,用于处理此类父/子链接,请参阅http://wiki.fasterxml.com/JacksonFeatureBiDirReferences.(Wayback Snapshot)

你当然可以使用大多数JSON处理包(jackson,gson和flex-json至少支持它)排除父链接的序列化,但真正的诀窍在于如何反序列化它(重新创建父链接),而不是只是处理序列化方面.虽然现在的声音只是排除可能对你有用.

EDIT(2012年4月):Jackson 2.0现在支持真正的身份引用(Wayback Snapshot),所以你也可以这样解决它.


eug*_*ene 12

在解决这个问题时,我采用了以下方法(在我的应用程序中标准化流程,使代码清晰可重用):

  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 {

}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

3)

static Gson createGsonFromBuilder( ExclusionStrategy exs ){
    GsonBuilder gsonbuilder = new GsonBuilder();
    gsonbuilder.setExclusionStrategies(exs);
    return gsonbuilder.serializeNulls().create();
}
Run Code Online (Sandbox Code Playgroud)

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...
}
Run Code Online (Sandbox Code Playgroud)

5)

在第一种情况下,null被提供给构造函数,您可以指定另一个要排除的类 - 下面添加了两个选项

Gson gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(null) );
Gson _gsonObj = createGsonFromBuilder( new GsonExclusionStrategy(Date.class) );
Run Code Online (Sandbox Code Playgroud)

6)

MyObjectToBeSerialized _myobject = someMethodThatGetsMyObject();
String jsonRepresentation = gsonObj.toJson(_myobject);
Run Code Online (Sandbox Code Playgroud)

或者,排除Date对象

String jsonRepresentation = _gsonObj.toJson(_myobject);
Run Code Online (Sandbox Code Playgroud)

  • 这会阻止子对象被处理,我可以包含子 json 然后停止循环吗 (2认同)

mat*_*t b 10

甚至可以用JSON表示双向关系吗?某些数据格式不适合某些类型的数据建模.

在处理遍历对象图时处理循环的一种方法是跟踪到目前为止您已经看到的对象(使用身份比较),以防止自己遍历无限循环.

  • 我很好奇 - 你会如何用JSON代表循环引用? (4认同)

小智 6

如果您使用 Jackon 进行序列化,只需将@JsonBackReference应用于您的双向映射即可解决循环引用问题。

注意:@JsonBackReference用于解决无限递归(StackOverflowError)