Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB serialized XML with ID references rather than containment

In a RestFul-Webservice (Jersey) context I need to marshal / serialize an Object graph to XML and JSON. For simplicity I try to explain the 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 a 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 multiple times. Now I add the uncommented XmlIDREF and XmlID annotations, which would result in XML similar to this:

<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 one lacks of information. How can I create (and unmarshal) 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 generic because I don't want to write extra classes for each new element in the object graph.

For completeness, here's the restful 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.

like image 714
steffen Avatar asked Nov 21 '12 18:11

steffen


1 Answers

If you are trying to serialize into a format that matches the last XML example you gave then I believe your object graph is structured incorrectly to achieve that.

If you want to provide a collection of Person objects with their associated houses and also provide a collection of the House objects then you need to return a serialized XML message which contains both collections. It appears as though you have your @XmlIDREF and @XmlID annotations in the correct place for making the Person-House association as you intend (based on your description) but you only return a collection of the Person objects as opposed to returning both collections.

Your webservice should look more like this (leaving out the serialization as it appears you're clear on 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 accomplish this (e.g. create a wrapper object which has collection attributes for people and houses, etc.) but hopefully you get the idea.

like image 165
Bionic_Geek Avatar answered Nov 10 '22 07:11

Bionic_Geek