去除双向递归关系的最简单方法是什么?

17

我使用 Gson 库将Java对象转换为Json响应...问题是,在JPA请求后,由于与其他实体存在递归关系,从数据库检索到的对象无法转换(请参见我的之前问题)例如:

public class Gps implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "IMEI", nullable = false, length = 20)
    private String imei;
    //some code here...
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "gpsImei", fetch = FetchType.LAZY)
    private List<Coordonnees> coordonneesList;


public class Coordonnees implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "IDCOORDONNEES", nullable = false)
    private Integer idcoordonnees;
    //some code here...
    @JoinColumn(name = "GPS_IMEI", referencedColumnName = "IMEI", nullable = false)
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    private Gps gpsImei;

我的源代码:

  EntityManagerFactory emf=Persistence.createEntityManagerFactory("JavaApplication21PU");
  GpsJpaController gjc=new GpsJpaController(emf);

  Gps gps=gjc.findGps("123456789012345");

  for(int i=0;i<gps.getCoordonneesList().size();i++){
   gps.getCoordonneesList().get(i).setGpsImei(null);
  }  

  Gson gson=new Gson();
  String json=gson.toJson(gps);//convert to json response

  System.out.println(json);  

正如您在这里看到的一样,我做了:

   for(int i=0;i<gps.getCoordonneesList().size();i++){
     gps.getCoordonneesList().get(i).setGpsImei(null);
   }  

只需通过在coordonneesList中的每个GPS对象上设置null来终止递归关系。

你认为这是一个好方法,还是有其他更实用的方法?谢谢。

5个回答

29

有一个名为GraphAdapterBuilder的Gson扩展程序,可序列化包含循环引用的对象。这是从相应测试用例中的一个非常简化的示例:

Roshambo rock = new Roshambo("ROCK");
Roshambo scissors = new Roshambo("SCISSORS");
Roshambo paper = new Roshambo("PAPER");
rock.beats = scissors;
scissors.beats = paper;
paper.beats = rock;

GsonBuilder gsonBuilder = new GsonBuilder();
new GraphAdapterBuilder()
    .addType(Roshambo.class)
    .registerOn(gsonBuilder);
Gson gson = gsonBuilder.create();
System.out.println(gson.toJson(rock));

这将打印:

{
  '0x1': {'name': 'ROCK', 'beats': '0x2'},
  '0x2': {'name': 'SCISSORS', 'beats': '0x3'},
  '0x3': {'name': 'PAPER', 'beats': '0x1'}
}

请注意,GraphAdapterBuilder类包含在gson.jar中。如果您想使用它,您需要手动将其复制到您的项目中。


1
谢谢!在使用Hibernate时,这真是一个救命稻草。在使用Gson转换Hibernate实体时,为我省去了很多麻烦。 - Knubo
4
哪个 jar 包含这个 "GraphAdapterBuilder" 类? - pavan
JSON字符串已经正确构建,但引用似乎无法正常工作。如何访问像0x1.beats.name这样的属性 --> 输出应该是'SCISSORS'?所以要在对象图中跳转。 - Tunguska
我能够自己解决这个问题。如果你有类似的问题,请查看:http://stackoverflow.com/a/22788445/2182503 - Tunguska
太棒了,节省了我很多时间。以后参考一下,只需从以下链接中复制GraphAdapterBuilder源代码:[ https://github.com/google/gson/blob/1d9e86e27c97cd85d898104b4ac42bb487d0d7d0/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java ],并直接在您的项目中使用它。 - Arthur
有人在此期间将gson-extras添加到Maven中央库;对于Gradle:compile 'org.danilopianini:gson-extras:0.2.1' - Rolf W.

2
我不知道Gson方面的情况,但我正在使用Jackson。查找一个使用它的ObjectMapper类的例子。至于递归,使用@JsonManagedReference和@JsonBackReference来阻止递归。查找这些注解的示例用法即可。

2
我会配置GsonBuilder以与jackson注释配合使用:
GsonBuilder builder = new GsonBuilder()
    .addSerializationExclusionStrategy(serializationExclusionStrategy);

final ExclusionStrategy serializationExclusionStrategy = new ExclusionStrategy() {
  @Override
  public boolean shouldSkipField(FieldAttributes f) {
    return f.getAnnotation(JsonIgnore.class) != null
           || f.getAnnotation(JsonBackReference.class) != null;
  }

  @Override
  public boolean shouldSkipClass(Class<?> aClass) {
    return false;
  }
};

非常感谢,伙计。这对我来说完美地解决了问题。即使你在2014年发布它,而我是在2020年使用的。 :) - Shafqat Shafi

1
你可以使用@Expose(serialize = false)来避免集合被序列化。

1

使用瞬态(transient)来忽略@OneToMany集合,以便于google.gson的解析。


如果这样做,大多数JPA实现将忽略OneToMany关联。 - Aleks Ben Maza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接