September 28, 2010

JAXB & Collection Properties

In this post will examine the different options JAXB offers for representing collections in XML.  We will look at the following annotations:
  • @XmlElement
  • @XmlElementWrapper
  • @XmlList
  • @XmlList and @XmlAttribute
  • @XmlList and @XmlValue


Java Model

For this example we will use the following model.   We will apply different JAXB annotations to observe the effect it has on the XML representation.

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    private List<String> emailAddresses;
    
    public Customer() {
        emailAddresses = new ArrayList<String>();
    }

    public List<String> getEmailAddresses() {
        return emailAddresses;
    }

    public void setEmailAddresses(List<String> emailAddresses) {
        this.emailAddresses = emailAddresses;
    }

}

Demo Code

The following code will be used to convert the Customer object to XML.  We will examine the impact of changing the metadata on the XML representation.

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

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

        Customer customer = new Customer();
        customer.getEmailAddresses().add("janed@example.com");
        customer.getEmailAddresses().add("jdoe@example.org");

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }
}

Default

By default each item in the collection will be marshalled to an XML element.

<customer>
    <emailAddresses>janed@example.com</emailAddresses>
    <emailAddresses>jdoe@example.org</emailAddresses>
</customer>

@XmlElement

We can control the name of the XML element a collection item is marshalled to by using the @XmlElement annotation.

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    @XmlElement(name="email-address")
    private List<String> emailAddresses;

}

The following is the corresponding XML output:

<customer>
    <email-address>janed@example.com</email-address>
    <email-address>jdoe@example.org</email-address>
</customer>

@XmlElementWrapper

Sometimes we want to add a grouping element to organize our collection data.  This is done using the @XmlElementWrapper annotation.

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    @XmlElementWrapper(name="email-addresses")
    @XmlElement(name="email-address")
    private List<String> emailAddresses;

}

The following is the corresponding XML output:

<customer>
    <email-addresses>
        <email-address>janed@example.com</email-address>
        <email-address>jdoe@example.org</email-address>
    </email-addresses>
</customer>

@XmlList

We can also represent our collection data as space seperated text.  This is done using the @XmlList annotation.

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    @XmlList
    private List<String> emailAddresses;

}

The following is the corresponding XML output:

<customer>
    <emailAddresses>janed@example.com jdoe@example.org</emailAddresses>
</customer>
 

@XmlList and @XmlAttribute

Since @XmlList allows us to represent a collection in a single piece of text it is also compatible with an XML attribute.

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    @XmlList
    @XmlAttribute
    private List<String> emailAddresses;

}

The following is the corresponding XML output:

<customer 
    emailAddresses="janed@example.com jdoe@example.org"/>

@XmlList and @XmlValue

Since @XmlList allows us to represent a collection in a single piece of text it is also compatible with a single text node.

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    @XmlList
    @XmlValue
    private List<String> emailAddresses;

}

The following is the corresponding XML output:

<customer>janed@example.com jdoe@example.org</customer>

17 comments:

  1. Thanks for posting. In a Spring application that I wrote using JAXB for marshalling, I chose to live with the ugly XML and have a nice Java API for the JAXB objects. Now I now that I can indeed have my cake and eat it to by using this metadata capability.

    ReplyDelete
  2. Thanks Sudhakar. The MOXy JAXB implementation has even more mapping options. Check out XPath Based Mapping - Geocode Example

    ReplyDelete
  3. Perfect - Just what I was looking for :)

    ReplyDelete
  4. Thanks for posting.
    Suppose we have List, Can we define Type of element present in List with annotation?

    ReplyDelete
  5. If you are using an untyped list, then you can specify the type using @XmlElement(type=Foo.class).

    -Blaise

    ReplyDelete
  6. You're a legend - I was looking for a summary just like this. Coming from an XStream background the marshalling done by JAXB seemed counter-intuitive at first...

    ReplyDelete
  7. Hi Blaise,
    Can we convert a JAXB object into DOCUMENT object or INPUTSTREAM object??If yes how??

    ReplyDelete
  8. Which annotation can I use for java's Map ?

    ReplyDelete
  9. JAXB has a default representation for java.util.Map. If you want more control of the XML format you can use an XmlAdapter:
    - XmlAdapter - JAXB's Secret Weapon

    I've also been considering the following extension to cover maps:
    - https://bugs.eclipse.org/322423

    -Blaise

    ReplyDelete
  10. You can create an org.w3c.dom.Document and use JAXB to marshal an object into it. You can also marshal object to something like a ByteArrayOutputStream and then create a ByteArrayInputStream from that. You may also be able to leverage javax.xml.bind.util.JAXBSource.

    -Blaise

    ReplyDelete
  11. Blaise, when unmarshalling is there a way of automatically applying @XmlElementWrapper to collections using Java 6 and JAXB 2.1?

    I had a look at Bjarne Hansen's plugin but this isn't compatible with JAXB 2.1

    ReplyDelete
  12. JAXB does not currently provide a standard way to generate an @XmlElementWrapper annotation when generating Java classes from an XML schema (although I think it should).

    I was not aware of Bjarne Hansen's plugin, I will take a look at it and post back. You may want to contact him about its use in JAXB 2.1.

    -Blaise

    ReplyDelete
    Replies
    1. Hello Blaise (sorry for cross-postings),

      Are there any developments on this front? I couldn't get this plugin to work with a recent xjc (2.2.4), and I would really like to tell xjc to skip certain levels and generate an @XmlElementWrapper instead.

      Thanks again for your tireless work in providing great and clear jaxb/xsd/xjc examples.

      ~Pierre

      Delete
    2. Hi Pierre,

      I'm happy to hear you are finding the blog useful. I haven't had a chance to investigate this plug-in yet. Have you tried contacting the author (Bjarne Hansen) of the plug-in?

      -Blaise

      Delete
  13. Thx for helpful examples. I was little bit nervous but now it works perfect. :)

    ReplyDelete
  14. I had been searching for a long time, and now found the solution in this post. Thank u very much!

    ReplyDelete
  15. And even years later your post helped me a lot :)
    Thanks for posting.

    ReplyDelete