JAXB serialized XML with identity links rather than containment

In the context of RestFul-Webservice (Jersey) I need to marshal / serialize the graph of objects in XML and JSON. For simplicity, I am trying to explain a problem with 2-3 classes:

Person.java

@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Person { private String name; // @XmlIDREF @XmlElement(name = "house") @XmlElementWrapper(name = "houses") private Collection<House> houses; public Person() {} public Person(String name, Collection<House> houses) { this.name = name; this.houses = houses; } } 

House.java

 @XmlAccessorType(XmlAccessType.FIELD) public class House { // @XmlID public String name; public String location; public House() {} public House(String name, String location) { this.name = name; this.location = location; } } 

Now when I serialize Person, the XML will look like this:

 <people> <person> <name>Edward</name> <houses> <house> <name>MyAppartment</name> <location>London</location> </house> <house> <name>MySecondAppartment</name> <location>London</location> </house> </houses> </person> <person> <name>Thomas</name> <houses> <house> <name>MyAppartment</name> <location>London</location> </house> <house> <name>MySecondAppartment</name> <location>London</location> </house> </houses> </person> </people> 

The problem here is that the same houses are listed several times. Now I am adding annotations without commenting on XmlIDREF and XmlID , which will cause the XML to look like the following:

 <people> <person> <name>Edward</name> <houses> <house>MyAppartment</house> <house>MySecondAppartment</house> </houses> </person> <person> <name>Thomas</name> <houses> <house>MyAppartment</house> <house>MySecondAppartment</house> </houses> </person> </people> 

While the first XML was too verbose, this was missing information. How can I create (and untie) something similar to:

 <people> <person> <name>Edward</name> <houses> <house>MyAppartment</house> <house>MySecondAppartment</house> </houses> </person> <person> <name>Thomas</name> <houses> <house>MyAppartment</house> <house>MySecondAppartment</house> </houses> </person> <houses> <house> <name>MyAppartment</name> <location>London</location> </house> <house> <name>MySecondAppartment</name> <location>London</location> </house> </houses> </people> 

The solution should be general, because I do not want to write additional classes for each new element in the object graph.

For completeness, here's a calm webservice:

 @Path("rest/persons") public class TestService { @GET @Produces({ MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) public Collection<Person> test() throws Exception { Collection<Person> persons = new ArrayList<Person>(); Collection<House> houses = new HashSet<House>(); houses.add(new House("MyAppartment", "London")); houses.add(new House("MySecondAppartment", "London")); persons.add(new Person("Thomas", houses)); persons.add(new Person("Edward", houses)); return persons; } } 

Thanks in advance.

+6
source share
1 answer

If you are trying to serialize in a format that matches the last XML example you gave, I assume that your object graph is not structured correctly to achieve this.

If you want to provide a collection of Person objects with their associated houses, as well as provide a collection of House objects, then you need to return a serialized XML message containing both collections. It seems that you have the @XmlIDREF and @XmlID in the right place to create the Person-House association of your intention (based on your description), but you only return a collection of Person objects as opposed to returning both collections.

Your web service should look more like this (excluding serialization, as you think you understand how to serialize it):

 @Path("rest/persons") public class TestService { @GET @Produces({ MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) public Map<String, Object> test() throws Exception { Map<String, Object> peopleAndHouses = new HashMap<String, Object>(); Collection<Person> persons = new ArrayList<Person>(); Collection<House> houses = new HashSet<House>(); houses.add(new House("MyAppartment", "London")); houses.add(new House("MySecondAppartment", "London")); persons.add(new Person("Thomas", houses)); persons.add(new Person("Edward", houses)); peopleAndHouses.put("houses", houses); peopleAndHouses.put("people", persons); return peopleAndHouses; } } 

There are other ways to achieve this goal (for example, creating a wrapper with collection attributes for people and homes, etc.), but I hope you get this idea.

+2
source

All Articles