5

So I am working on a RESTful data API using Java, Hibernate, JPA annotations, JAX-RS annotations, JAXB annotations, Jersey, and the Jackson JSON parser.

After trying various configurations of the MAPPING and NATURAL JSON notations that Jersey provides, I finally decided to use the Jackson JSON parser instead. Jackson would be perfect except for this one issue...

The issue I have come across is that Jackson does not work with the JAXB annotations, "@XmlID" and "@XmlIDREF", which I use to signify my entity relationships, and although the "@JsonBackReference" and "@JsonManagedReference" help with this. The combination seems to break down when dealing with direct self-referencing properties.

This seems like it would be a fairly common problem. How have any of you circumvented this limitation with Jackson?

With my POJO as...

@XmlRootElement
public class Employee implements Serializable {
    private Date lastUpdatedOn;
    private Employee lastUpdatedBy;
    private Integer empId;

    @JoinColumn(nullable=false)
    @OneToOne
    @XmlIDREF
    public Employee getLastUpdatedBy() {
        return createdBy;
    }
    public void setLastUpdatedBy(Employee lastUpdatedBy) {
        this.lastUpdatedBy = lastUpdatedBy;
    }

    @Temporal(TemporalType.TIMESTAMP)
    public Date getLastUpdatedOn() {
        return createdOn;
    }
    public void setLastUpdatedOn(Date lastUpdatedOn) {
        this.lastUpdatedOn = lastUpdatedOn;
    }
    @XmlID
    @XmlJavaTypeAdapter(IntegerAdapter.class)
    public Integer getEmpId() {
        return empId;
    }
    public void setEmpId(Integer empId) {
        this.empId = empId;
    }
}

... and the following EmployeeResource...

@Path("/Employees")
public class EmployeeResource {
  private List<Employee> employees;

  public List<Employee> getEmployees() {
    return employees;
  }
  public void setEmployees(List<Employee> employees) {
    this.employees = employees;
  }

  @GET
  @Path("/{empId}")
  public Response getEmployees(
    @Context UriInfo ui
    , @PathParam("id") Integer empId
  ) {
    this.employees = HibernateUtil.pagedQuery(
      Employee.class
      , new ArrayList() {
        Restrictions.eq("empId",empId)
      }
    );
    return Response.ok(this).build();
  }
}

My JAX-RS resource will produce the following error

org.codehaus.jackson.map.JsonMappingException: Direct self-reference leading to cycle (through reference chain: resources.EmployeeResource["employees"]->java.util.ArrayList[0]->entities.Employee["lastUpdatedBy"])

... but I want it to produce...

{
  "employees" : [ {
    "lastUpdatedOn" : 1331149770737,
    "lastUpdatedBy" : 10150,
    "empId" : 10150,
  } ],
}

Thanks in advance, everyone!

NOTES:

  1. I use the IntegerAdapter.class to convert it to a string so that it will work with the @XmlID annotation.
  2. The Employee and EmployeeResource classes described above are merely abbreviated versions of my actual implementation, but they represent the portion of my implementation that is relevant to this direct self-referencing issue.

EDIT #1 2012.03.10 I'm sorry, in my first version of this question I had gotten confused between the version I have using Jersey's natural notation vs the version I have running with Jackson. I have revised my question to more accurately reflect the direct self-referencing issue I have with Jackson.

hypno7oad
  • 1,441
  • 1
  • 19
  • 28
  • I think your question is not relevant to `@XmlID` as is, but to JSON basics. JSON does not support types, so `"10150"` (string) and `10150` (integer, but not supported) are "the same". – dma_k Mar 10 '12 at 13:50
  • 2
    I'd have to disagree. JSON has an understanding of types such as Object, Array, String and number, www.json.org . – hypno7oad Mar 10 '12 at 14:39
  • I was wrong sorry. What you need I believe is the converter from `Date` to `int`. See example [here](http://blog.seyfi.net/2010/03/how-to-control-date-formatting-when.html). – dma_k Mar 10 '12 at 17:59
  • Sorry @dma_k, I had gotten confused when writing up the question. I've tried to go back and be more specific. Initially I was just using Jersey's natural annotation, which didn't handle Dates well. Now that I'm using Jackson, all the strings, dates(timestamps) and numbers are serialized correctly. However, I now have this direct self-referencing issue, which was not an issue when Jersey would use the JAXB annotations, "@XmlID" and "@XmlIDREF". – hypno7oad Mar 10 '12 at 20:11

1 Answers1

4

Jackson 1.x does not have specific support for resolving cyclic references, but there is support for handling parent/child style dependencies: this blog entry has more info.

Jackson 2.0 will have support for arbitrary Object Id / reference handling, using new @JsonIdentityInfo annotation, so perhaps that could be used to solve the problem. Official 2.0 release is not out yet, but release candidates (latest being RC2) are, in case you wanted to have a look. Not sure if it would handle your problem, but maybe it would help.

EDIT: Actually, Jackson JAXB annotation module will have support for @XmlID / @XmlIDREF for 2.0.0 -- this was just implemented, see here.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • Thanks for the link. I'll take a look at RC2. Although it's a little disappointing that they can't use the JAXB annotations for this much like Jersey does. – hypno7oad Mar 12 '12 at 20:53
  • JAXB annotations are bit XML-specific, so for Jackson they are secondary ones -- supported, but have to be fitted to work through native mechanisms. FWIW, there is now this: [https://github.com/FasterXML/jackson-module-jaxb-annotations/issues/2] which tracks possibility of implementing JAXB annotations... – StaxMan Mar 12 '12 at 23:37
  • How can one debug whether the `@XmlIDREF` annotation is in place? Here, in my code, it jumps to [com.fasterxml.jackson.databind.ser.BeanPropertyWriter#serializeAsField](https://github.com/FasterXML/jackson-databind/blob/jackson-databind-2.2.2/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java#L534), which just serializes the content of the field and does NOT use the ID of the referenced element. – koppor Jun 20 '17 at 16:15