6
votes

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!

1
Have you seen this question? stackoverflow.com/questions/3073364/…Tomalak
Thanks, I'm looking into it, but it still leaves JSON out in the open, no?Stuarty
I'm guessing that there must be an analogous approach for JSON. (I don't know, though. The question sounded reasonably similar so I wanted to cross-link them.)Tomalak

1 Answers

5
votes

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