javaxmlspring-bootmarshallingjavax.xml

How to serialize a list of Java Object to XML using javax.xml library with proper parent/child node names


I have a list called Documents that is comprised of different Document objects I created. I am trying to serialize the list to look like this:

<Documents>
   <Document>
   .
   .
   .
   </Document>
   <Document>
   .
   .
   .
   </Document>
</Documents>

This is is what I have in code:

    @XmlElementWrapper(name="Documents")
    @XmlElement(name = "Document")
    private Document[] documents;

But this is what its serializing to:

   <Documents>
       <Documents>
       .
       .
       .
       </Documents>
       <Documents>
       .
       .
       .
       </Documents>
    </Documents>

Where the children nodes have the same name as the parent node. IS there a way to get the desired outcome?


Solution

  • to ensures the XML is serialized correctly with proper parent and child node names you just need to modify the Document class to include a wrapper element for the list of documents. here is a test code so you get the point

    Document Class

    package com.stackoverflow.xmlserialization;
    
    import jakarta.xml.bind.annotation.XmlElement;
    import jakarta.xml.bind.annotation.XmlRootElement;
    import jakarta.xml.bind.annotation.XmlType;
    import java.util.List;
    
    @XmlRootElement(name = "Documents")
    @XmlType(name = "Document")
    public class Document {
        private String title;
        private String content;
        private List<Document> documents;
    
        public Document() {
        }
    
        public Document(String title, String content) {
            this.title = title;
            this.content = content;
        }
    
        @XmlElement(name = "Title")
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        @XmlElement(name = "Content")
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        @XmlElement(name = "Document")
        public List<Document> getDocuments() {
            return documents;
        }
    
        public void setDocuments(List<Document> documents) {
            this.documents = documents;
        }
    }
    

    DocumentController Class

    package com.stackoverflow.xmlserialization;
    
    import jakarta.xml.bind.JAXBContext;
    import jakarta.xml.bind.JAXBException;
    import jakarta.xml.bind.Marshaller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.io.StringWriter;
    import java.util.Arrays;
    import java.util.List;
    
    @RestController
    public class DocumentController {
    
        @GetMapping(value = "/documents", produces = "application/xml")
        public String getDocuments() {
            // Create a hardcoded list of Document objects
            List<Document> documents = Arrays.asList(
                    new Document("Document 1 Title", "Document 1 Content"),
                    new Document("Document 2 Title", "Document 2 Content")
            );
    
            // Create a wrapper Document object to hold the list
            Document wrapper = new Document();
            wrapper.setDocuments(documents);
    
            try {
                // Initialize JAXB context and marshaller
                JAXBContext context = JAXBContext.newInstance(Document.class);
                Marshaller marshaller = context.createMarshaller();
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    
                // Serialize to XML
                StringWriter sw = new StringWriter();
                marshaller.marshal(wrapper, sw);
    
                // Return the XML string
                return sw.toString();
    
            } catch (JAXBException e) {
                e.printStackTrace();
                return "<error>Error generating XML</error>";
            }
        }
    }
    

    I've test the /documents endpoint, and the returned xml is:

    <Documents>
        <Document>
            <Content>Document 1 Content</Content>
            <Title>Document 1 Title</Title>
        </Document>
        <Document>
            <Content>Document 2 Content</Content>
            <Title>Document 2 Title</Title>
        </Document>
    </Documents>
    

    and this is the desired structure.