I'm using MOXy with Jersey to implement a RESTful API and want to return lists naturally for JSON and XML, by which I mean that the XML contains an element tag for the overall collection as well as the collection items, whereas the JSON contains a tag for the collection only.
For example, I want to return an "organisation" resource with nested lists of facilities and locations. As XML:
<organisation>
<id>1</id>
<name>XYZ</name>
<facilities>
<facility>
<id>1</id>
<text>Telephone</text>
</facility>
<facility>
<id>3</id>
<text>Whiteboard</text>
</facility>
</facilities>
<locations>
<location>
<id>1</id>
<kind>REGION</kind>
<name>London</name>
</location>
<location>
<id>2</id>
<kind>REGION</kind>
<name>Manchester</name>
</location>
</locations>
</organisation>
And as JSON:
{
"id": 1,
"name": "XYZ",
"facilities": [
{
"id": 1,
"text": "Telephone"
},
{
"id": 3,
"text": "Whiteboard"
}
],
"locations": [
{
"id": 1,
"kind": "REGION",
"name": "London"
},
{
"id": 2,
"kind": "REGION",
"name": "Manchester"
}
]
}
Unfortunately, I don't seem to be able to get a code base that allows me to return the output like this for both XML and JSON. If I use a class to wrap the nested list, then the XML appears correct but not the JSON (see "ExternalFacilities" below). If I define the nested list as an ArrayList
sub-class then the JSON appears correct but not the XML (see "ExternalLocations" below).
XML sample showing "facilities" list correct but not "locations"
See that there is no XML element that wraps the "locations" list (as there is for "facilities"), and also that each location has a pluralised element name.
<organisation>
<id>1</id>
<name>XYZ</name>
<facilities>
<facility>
<id>1</id>
<text>Telephone</text>
</facility>
<facility>
<id>3</id>
<text>Whiteboard</text>
</facility>
</facilities>
<locations>
<id>1</id>
<kind>REGION</kind>
<name>London</name>
</locations>
<locations>
<id>2</id>
<kind>REGION</kind>
<name>Manchester</name>
</locations>
</organisation>
JSON sample showing "locations" list correct but not "facilities"
See that the "facilities" list is a JSON object containing a JSON array, whereas I just want the JSON array (with the pluralised element name).
{
"id": 1,
"name": "XYZ",
"facilities": {
"facility": [
{
"id": 1,
"text": "Telephone"
},
{
"id": 3,
"text": "Whiteboard"
}
]
},
"locations": [
{
"id": 1,
"kind": "REGION",
"name": "London"
},
{
"id": 2,
"kind": "REGION",
"name": "Manchester"
}
]
}
This samples above are produced using the same code, just changing the Accept HTTP header to have Jersey return JSON instead of XML. Here is an excerpt of the classes:
ExternalOrganisation.java
@XmlRootElement(name="organisation")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExternalOrganisation {
private String name;
private ExternalFacilities facilities;
private ExternalLocations locations;
...
}
ExternalFacilities.java
@XmlRootElement(name="facilities")
public class ExternalFacilities {
@XmlElementRef
private List<ExternalFacility> list;
...
}
ExternalLocations.java
@XmlRootElement(name="locations")
public class ExternalLocations extends ArrayList<ExternalLocation> {
...
}
ExternalFacility.java
@XmlRootElement(name="facility")
@XmlType(propOrder={"id", "uri", "kind", "text"})
public class ExternalFacility extends ExternalBase {
// id inherited
private String text;
....
}
ExternalLocation.java
@XmlRootElement(name="location")
@XmlType(propOrder={"id", "kind", "name"})
public class ExternalLocation extends ExternalBase {
// id inherited
@XmlElement private LocationKind kind;
@XmlElement private String name;
...
}
Seems at first similar to this question but I'm not trying to mix the types of object in my lists.
You could do the following to get the desired XML and JSON representations:
@XMLElementWrapper
Instead of:
@XmlRootElement(name="organisation")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExternalOrganisation {
private String name;
private ExternalFacilities facilities;
private ExternalLocations locations;
...
}
You could do the following with @XmlElementWrapper
(see: http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html):
@XmlRootElement(name="organisation")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExternalOrganisation {
private String name;
@XmlElementWrapper
@XmlElementRef
private List<ExternalFacility> facilities;
@XmlElementWrapper
@XmlElementRef
private List<ExternalLocation> locations;
...
}
By specifying the wrapper as array name property, MOXy will use the value from @XmlElementWrapper
as the JSON array name.
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class YourApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(1);
set.add(YourService.class);
return set;
}
@Override
public Set<Object> getSingletons() {
MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider();
moxyJsonProvider.setWrapperAsArrayName(true);
HashSet<Object> set = new HashSet<Object>(1);
set.add(moxyJsonProvider);
return set;
}
}
For More Information
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With