Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialize bi-directional JPA entities to JSON with jackson

I'm using Jackson to serialize my JPA model into JSON.

I have the following classes:

import com.fasterxml.jackson.annotation.*;
import javax.persistence.*;
import java.util.Set;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
@Entity
public class Parent {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;

  @JsonManagedReference
  @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
  private Set<Child> children;

  //Getters and setters
}

and

import com.fasterxml.jackson.annotation.*;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
@Entity
public class Child {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;

  @JsonBackReference
  @ManyToOne
  @JoinColumn(name = "parentId")
  private Parent parent;

  //Getters and setters
}

I'm using the POJO mapping to serialize from model to JSON. When I serialize a Parent object I get the following JSON:

{
  "id": 1,
  "name": "John Doe",
  "children": [
    {
      "id": 1,
      "name": "child1"
    },{
      "id": 2,
      "name": "child2"
    }
  ]
}

But when I serialize a Child I get the following JSON:

{
  "id": 1,
  "name": "child1"
}

The reference to the parent is missing. Is there a way to solve this?

like image 692
mmjmanders Avatar asked Mar 24 '14 16:03

mmjmanders


5 Answers

I think you have to choose between the @JsonIdentityInfo and the @JsonBackReference / @JsonManagedReference.

I would go with : @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id") on your entities, removes @JsonBackReference / @JsonManagedReference pairs.

And add @JsonIgnore on the fields you want to exclude.

like image 95
GSP59 Avatar answered Nov 19 '22 23:11

GSP59


You can use JsonManagedReference / JsonBackReference and at the same time use JsonIdentityInfo to complement bidirectional relationships.

In question Class:

// bi-directional one-to-many association to Answer (Question is owner)
@JsonManagedReference
@OneToMany(mappedBy = "question", cascade = CascadeType.ALL)
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@QuestionAnswers")
private Set<Answer> answers = new HashSet<>();

In answer Class: // bi-directional many-to-one association to Question

@JsonBackReference
@ManyToOne
@JoinColumn(name = "questionId", referencedColumnName="id", foreignKey = @ForeignKey(name = "fk_answer_question"))
private Question question;

If you need parent reference in child object, remove Managed / Back Reference, this works fine for me.

like image 35
Jorge Avatar answered Nov 19 '22 22:11

Jorge


The problem is that use of managed/back references requires that direction of traversal is always from parent to child (that is, using managed reference first). This is a limitation for these annotations.

As the other answer suggests, use of Object Ids is the more flexible alternative that could perhaps work.

One other option that could perhaps work would be to use JSON Views or JSON Filter to conditionally include/exclude parent reference, if you can separate cases. This could get messy.

like image 4
StaxMan Avatar answered Nov 19 '22 23:11

StaxMan


@JsonIgnoreProperties({"excludedPropertyName"}) can do the job.

@Entity
public class Parent {

    @JsonIgnoreProperties({"parent"})
    @OneToMany(mappedBy = "parent")
    private List<Child> children; 

    // ...
}
@Entity
public class Child {

    @JsonIgnoreProperties({"children"})
    @ManyToOne
    private Parent parent;

    // ...
}
like image 4
Radovan Avatar answered Nov 19 '22 23:11

Radovan


You can use @JsonBackReference/@JsonManagedReference and add this method to child

@JsonProperty
public Long getParentId() {
    return parent == null ? null : parent.getId();
}

Result will be:

{
  "id": 1,
  "name": "child1",
  "parentId": 1
}

I hope this helps someone.

like image 3
liy Avatar answered Nov 19 '22 22:11

liy