How to annotate a list using @XmlElement?

52,903

Solution 1

You need to leverage @XmlElementWrapper and @XmlElement.

Java Model

Content

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

@XmlRootElement
public class Content {

    private List<String> keywords;

    public Content() {}

    @XmlElementWrapper
    @XmlElement(name="keyword")
    public List<String> getKeywords() {
        return keywords;
    }

    public void setKeywords(List<String> keywords) {
        this.keywords = keywords;
    }  

}

Demo Code

Demo

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

public class Demo {

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

        List<String> strings = new ArrayList<String>(2);
        strings.add("foo");
        strings.add("bar");

        Content content = new Content();
        content.setKeywords(strings);

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

}

Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<content>
    <keywords>
        <keyword>foo</keyword>
        <keyword>bar</keyword>
    </keywords>
</content>

For More Information

Below are links to a couple articles from my blog that provide additional information:

Solution 2

Use this form:

@XmlElementWrapper(name="keywords")
@XmlElement(name="keyword")

Please note that if keywords is empty then you will get <keywords />.

Sometimes you will need to add @XmlRootElement to your class (depends on the context) and the @XmlAccessorType(XmlAccessType.?) annotation. I usually use @XmlAccessorType(XmlAccessType.FIELD) and annotate my fields with @XmlElement.

Share:
52,903
user3429010
Author by

user3429010

Updated on November 07, 2020

Comments

  • user3429010
    user3429010 over 3 years

    I have the following annotation using javax.xml.bind.annotation.XmlElement:

    @XmlElement         
    public List<String> getKeywords() {
        return keywords;
    }
    

    Which produces the following XML when I marshall some example content:

    <keywords>keyword1</keywords>
    <keywords>keyword2</keywords>
    

    I would like to get the following XML:

    <keywords>
        <keyword>keyword1</keyword>
        <keyword>keyword2</keyword>
    </keywords>
    

    What kind of an annotation should I use?

    I've tried

    @XmlElementWrapper
    @XmlElement(name="keyword")
    

    But then the whole content disappears and the result is:

    <keywords/>
    

    The same happens also if I only try to rename the element:

    @XmlElement(name="keyword")
    

    What am I doing wrong?

    UPDATE:

    Here is the updated full code for the class according to the first answers, but it is still not working (the result is an empty list <keywords/> when marshalled to XML):

    import java.util.List;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlElementWrapper;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    public class Content {
    
        private List<String> keywords;
    
        public Content() {}
    
        @XmlElementWrapper(name="keywords")
        @XmlElement(name="keyword")
        public List<String> getKeywords() {
            return keywords;
        }
    
        public void setKeywords(List<String> keywords) {
            this.keywords = keywords;
        }  
    
    }
    

    I also tried the following with the same result:

    import java.util.List;
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlElementWrapper;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Content {
    
        @XmlElementWrapper(name="keywords")
        @XmlElement(name="keyword")
        private List<String> keywords;
    
        public Content() {}
    
        public List<String> getKeywords() {
            return keywords;
        }
    
        public void setKeywords(List<String> keywords) {
            this.keywords = keywords;
        }  
    
    }
    

    However, the keywords are not empty as the following produces <keywords>keyword1</keywords><keywords>keyword2</keywords> instead of an empty list:

    import java.util.List;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlElementWrapper;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    public class Content {
    
        private List<String> keywords;
    
        public Content() {}
    
        @XmlElement
        public List<String> getKeywords() {
            return keywords;
        }
    
        public void setKeywords(List<String> keywords) {
            this.keywords = keywords;
        }  
    
    }
    

    The code for marshalling is (JAX-RS):

    import java.io.StringWriter;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.Path;
    import javax.ws.rs.POST;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Marshaller;
    
    @Path("process")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_XML)
    public class ContentHandler {
    
        @POST
        public Response process(Content content) {
    
            StringWriter stringWriter = new StringWriter();
            try {
                JAXBContext jaxbContext = JAXBContext.newInstance(Content.class);
                Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
                jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                jaxbMarshaller.marshal(content, stringWriter);
            } catch (JAXBException e) {
                return Response.serverError().entity(e.getMessage()).build();
            }
            return Response.ok(stringWriter.toString(), MediaType.APPLICATION_XML).build();       
        }
    
    }