Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAX RS - JSON and XML Circular/Cyclic reference error

I'm using JAX RS to create a REST webservice using the usual @Path, @GET, @Produces({"application/json, "application/xml"}).

I'm returning a POJO as response which is sent as JSON or XML depending upon the type of request. It was working fine untill I added a Many-To-Many relationship with another entity. The relationship is bidirectional.

I'm using JBoss AS 7. I added Jackson's @JsonManagedReference and @JsonBackReference but to no avail.

How to get over this?

I deployed my JAX RS like this:-

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_3_0.xsd" version="3.0">
    <servlet>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>javax.ws.rs.core.Application</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>    

I didn't extend any Application class or used any JAXRS activator class.

This JBoss's RESTEasy is using Jackson as their JSON provider, even then why it is not recognising @JsonManagedReference annotations?

Do I have to update the dependencies, if yes then how? And how to fix if the request is of XML, there too it fails in circular reference in JAXB.

Thanks in advance!

like image 653
Stuarty Avatar asked Sep 07 '12 05:09

Stuarty


People also ask

How to send JAX RS @consumes with both XML and JSON example?

To send a request in JSON format, we will send “Content-Type: application/json” key-value in the header of the request. To send a request in XML format, we will send “Content-Type: application/xml” key-value in the header of the request. That’s all for JAX RS @Consumes with both XML and JSON Example.

How to deal with circular references in jsonserializer?

See also the DataContractsSerializer class. Here is an example custom Jackson JSONSerializer that deals with circular references by serializing the first occurrence and storing a * reference to the first occurrence on all subsequent occurrences.

Why do I get a circular reference error when returning jsonresult?

When returning a "parent" object via a JsonResult a circular reference error is thrown because "child" has a property of class parent. I have tried the ScriptIgnore attribute but I lose the ability to look at the child objects. I will need to display information in a parent child view at some point.

How to get JSON and XML response in Java?

Create the Java classes Produces_XML_JSON_Example.java and Student.java under com.javainterviewpoint folder. The getBothResponse () method is capable of producing both xml and json response, if the client is requesting for an xml response it will produce xml response, if json then it will produce json response.


1 Answers

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

MOXy offers the @XmlInverseReference extension that can be used to support bidirectional relationships in both XML and JSON binding.


JAVA MODEL

Customer

Customer has a collection of PhoneNumber objects.

package forum12312395;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement
public class Customer {

    private List<PhoneNumber> phoneNumbers;

    @XmlElement(name="phone-number")
    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

}

PhoneNumber

Each PhoneNumber object maintains a back pointer to the Customer object. This property is annotated with @XmlInverseReference.

package forum12312395;

import javax.xml.bind.annotation.XmlValue;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;

public class PhoneNumber {

    private String value;
    private Customer customer;

    @XmlValue
    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @XmlInverseReference(mappedBy="phoneNumbers")
    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

}

jaxb.properties

To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

INPUT

Below are the documents that we will unmarshal in this example

input.xml

<?xml version="1.0" encoding="UTF-8"?>
<customer>
    <phone-number>555-WORK</phone-number>
    <phone-number>555-HOME</phone-number>
</customer>

input.json

{
    "customer" : {
        "phone-number" : ["555-HOME", "555-WORK"]
    }
}

DEMO

package forum12312395;

import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;
import org.eclipse.persistence.oxm.MediaType;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        // JSON
        Unmarshaller jsonUnmarshaller = jc.createUnmarshaller();
        jsonUnmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
        StreamSource json = new StreamSource("src/forum12312395/input.json");
        Customer customerFromJSON = (Customer) jsonUnmarshaller.unmarshal(json);
        for(PhoneNumber phoneNumber : customerFromJSON.getPhoneNumbers()) {
            System.out.println(phoneNumber.getCustomer());
        }

        // XML
        Unmarshaller xmlUnmarshaller = jc.createUnmarshaller();
        StreamSource xml = new StreamSource("src/forum12312395/input.xml");
        Customer customerFromXML = (Customer) xmlUnmarshaller.unmarshal(xml);
        for(PhoneNumber phoneNumber : customerFromXML.getPhoneNumbers()) {
            System.out.println(phoneNumber.getCustomer());
        }
    }

}

OUTPUT

Below is the output from running the demo code. As you can see the customer property is populated on all of the PhoneNumber objects.

forum12312395.Customer@3ef38fd1
forum12312395.Customer@3ef38fd1
forum12312395.Customer@320eef20
forum12312395.Customer@320eef20

FOR MORE INFORMATION

  • http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
  • http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
  • http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
  • http://blog.bdoughan.com/2011/08/binding-to-json-xml-geocode-example.html
like image 143
bdoughan Avatar answered Nov 05 '22 12:11

bdoughan