17

I use the Gson library to convert Java objects to a Json response... the problem is that after a JPA requests the object retrieved from DB can not be converted because of a recursive relationship with other entities(see my previous question) for example :

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;

My source code:

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

As you can see here i made :

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

only to kill the recursive relationship by setting null for each GPS object in the coordonneesList..

In your opinion this is a good solution or is there another method more practical? Thanks

Community
  • 1
  • 1
Marwen Trabelsi
  • 4,167
  • 8
  • 39
  • 80

5 Answers5

29

There's a Gson extension called GraphAdapterBuilder that can serialize objects that contain circular references. Here's a very simplified example from the corresponding test case:

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

This prints:

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

Note that the GraphAdapterBuilder class is not included in gson.jar. If you want to use it, you'll have to copy it into your project manually.

approxiblue
  • 6,982
  • 16
  • 51
  • 59
Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
  • 1
    Thanks! This was a lifesaver when working with hibernate. Saved me a bunch of trouble when converting an hibernate entity using gson. – Knubo Oct 02 '12 at 11:21
  • 4
    which jar contains this "GraphAdapterBuilder" class ? – pavan Dec 30 '13 at 10:47
  • The json string is properly builded, but the references don't seem to be working. How can access a property like 0x1.beats.name --> output should be 'SCISSORS'? So jump within the object graph. – Tunguska Mar 31 '14 at 12:00
  • I was able to solve the problem by myself. If you have a similar problem please check: http://stackoverflow.com/a/22788445/2182503 – Tunguska Apr 01 '14 at 14:14
  • awesome, saved me lots of time. For future reference, just copy the GraphAdapterBuilder source from: [ https://github.com/google/gson/blob/1d9e86e27c97cd85d898104b4ac42bb487d0d7d0/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java ] and use it directly in your project – Arthur Feb 15 '17 at 11:39
  • Someone added gson-extras to maven central in the mean time; For Gradle: `compile 'org.danilopianini:gson-extras:0.2.1'` – Rolf W. Feb 26 '17 at 19:49
2

I don't know about with Gson but I'm working with Jackson. Look up an example of using its ObjectMapper class. As for the recursion, use @JsonManagedReference and @JsonBackReference to stop that. Look those up for example usage too.

Javateer
  • 21
  • 1
2

I would configure GsonBuilder to work with jackson annotations:

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;
  }
};
raisercostin
  • 8,777
  • 5
  • 67
  • 76
1

You can use @Expose(serialize = false) to avoid the Collection to be serialized.

Navin S
  • 5
  • 2
1

Use transient for ignoring @OneToMany sets for google.gson

Zhurov Konstantin
  • 712
  • 1
  • 8
  • 14