JAXB: How to marshal objects in lists?

97,069

Solution 1

On the records property add:

@XmlElementWrapper(name="records")
@XmlElement(name="data")

For more information on JAXB and collection properties see:

Solution 2

This is in response to your second question disquised an answer:

Both approaches will generate the same XML. My recommendation is go with the model that is best for your application. For me that is generally using @XmlElementWrapper/@XmlElement. Since "records" is just there to organize the "data" elements it doesn't really deserve its own class.

I lead the MOXy JAXB implementation and we offer an XPath-based mapping extension to go beyond what is capable with @XmlElementWrapper:

Solution 3

In response to your second question:

Is this the recommended way to create a Java class structure
according to the XML file structure above?

Technically speaking, introducing an extra Records class to solve your JAXB issue is unnecessary and redundant work, because JAXB does not need it. The @XmlElementWrapper and @XmlElement name property have been designed to solve your issue.

From your comments to Blaise's answer, I maintain a tutorial with operational examples explaining how do deal with generic classes such as List, etc.. when unmarshalling.

Share:
97,069
rmv
Author by

rmv

Updated on July 05, 2022

Comments

  • rmv
    rmv almost 2 years

    Perhaps a stupid question: I have a List of type <Data> which I want to marshal into a XML file. This is my class Database containing an ArrayList...

    @XmlRootElement
    public class Database
    {
        List<Data> records = new ArrayList<Data>();
    
        public List<Data> getRecords()                   { return records; }
        public void       setRecords(List<Data> records) { this.records = records; }
    }
    

    ...and this is class Data:

    // @XmlRootElement
    public class Data 
    {
        String name;
        String address;
    
        public String getName()            { return name;      }
        public void   setName(String name) { this.name = name; }
    
        public String getAddress()               { return address;         }
        public void   setAddress(String address) { this.address = address; }
    }
    

    Using the following test class...

    public class Test
    {
        public static void main(String args[]) throws Exception
        {
            Data data1 = new Data();
                 data1.setName("Peter");
                 data1.setAddress("Cologne");
    
            Data data2 = new Data();
                 data2.setName("Mary");
                 data2.setAddress("Hamburg");
    
            Database database = new Database();
                     database.getRecords().add(data1);
                     database.getRecords().add(data2);
    
            JAXBContext context = JAXBContext.newInstance(Database.class);
            Marshaller marshaller = context.createMarshaller();
                       marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                       marshaller.marshal(database, new FileWriter("test.xml"));       
        }
    }
    

    ...I got the result:

    <database>
        <records>
            <address>Cologne</address>
            <name>Peter</name>
        </records>
        <records>
            <address>Hamburg</address>
            <name>Mary</name>
        </records>
    </database>
    

    But that's not what I was expecting, i.e. all tags for <Data> objects are missing. I am looking for a way to export the data in the following structure, but I don't know how to achieve this:

    <database>
        <records>
            <data>
                <address>Cologne</address>
                <name>Peter</name>
            </data>
            <data>
                <address>Hamburg</address>
                <name>Mary</name>
            </data>
        </records>
    </database>
    

    One additional question: if I want to deal with the problem without using @XmlElementWrapper and @XmlElement annotations, I can introduce an intermediary class

    public class Records
    {
        List<Data> data = new ArrayList<Data>();
    
        public List<Data> getData()                { return data; }
        public void       setData(List<Data> data) { this.data = data; }
    }
    

    used by the modified base class

    @XmlRootElement
    public class Database
    {
        Records records = new Records();
    
        public Records getRecords()                { return records; }
        public void    setRecords(Records records) { this.records = records; }
    }
    

    in a slightly modified Test class:

    ...
    Database database = new Database();
    database.getRecords().getData().add(data1);
    database.getRecords().getData().add(data2);
    ...
    

    The result also is:

    <database>
        <records>
            <data>
                <address>Cologne</address>
                <name>Peter</name>
            </data>
            <data>
                <address>Hamburg</address>
                <name>Mary</name>
            </data>
        </records>
    </database>
    

    Is this the recommended way to create a Java class structure according to the XML file structure above?

  • rmv
    rmv over 13 years
    Cool, that's it! Thanks a lot.
  • rmv
    rmv over 13 years
    It's really quite impressive what's going on in this area. I think i need some time to get in touch with this new mapping technique. Can you give me a first hint, how to read exported XML data into the class structure again? If i use @XmlElementWrapper/@XmlElement for the export, the import program complains that the corresponding classes are (obviously :-) not existing...
  • bdoughan
    bdoughan over 13 years
    The import (unmarshal) should just work, what exception are you getting?
  • rmv
    rmv over 13 years
    I think i got an exception that class 'Records' could not be find. But to be sure, i will verify it by an example tomorrow...
  • rmv
    rmv over 13 years
    Yes, you are right, unmarshalling works as expected! --- JAXBContext context = JAXBContext.newInstance(Database.class); Unmarshaller unmarshaller = context.createUnmarshaller(); Database database = (Database) unmarshaller.unmarshal(new File("test.xml"));
  • bdoughan
    bdoughan over 12 years
    @LawrenceTierney - The following article on @XmlAccessorType may help with annotating fields or properties (getter/setter): blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.htm‌​l
  • Alkanshel
    Alkanshel over 6 years
    I would have posted this on your blog but it seems like only team members can respond there. Thank you for your sample code, but is there a cleaner way to do this now in 2017 without the boilerplate classes and strict typing?
  • Roberto
    Roberto over 5 years
    @XmlelementWrapper is not applicable to type - compilation error message. What's wrong?