简体   繁体   中英

Unmarshalling complex nested list of xml items using JAXB

I am trying to unmarshall this complex xml, but not able to do it successfully. Below is my xml:

<ImportSession xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Batches>
    <Batch BatchClassName="BC">
      <BatchFields>
        <BatchField Name="N11" Value="N21"/>
        <BatchField Name="N12" Value="N22"/>
        <BatchField Name="N13" Value="N23"/>
        <BatchField Name="N14" Value="N24"/>
        <BatchField Name="N15" Value="N25"/>
      </BatchFields>
      <Documents>
        <Document FormTypeName="F1">
          <IndexFields>
            <IndexField Name="NM11" Value="V11"/>
            <IndexField Name="NM12" Value="V12"/>
            <IndexField Name="NM13" Value="V13"/>
            <IndexField Name="NM14" Value="V14"/>
         </IndexFields>
         <Pages>
           <Page ImportFileName="P1.pdf"/>
         </Pages>
       </Document>
       <Document FormTypeName="F2">
         <IndexFields>
           <IndexField Name="NM21" Value="V21"/>
           <IndexField Name="NM22" Value="V22"/>
           <IndexField Name="NM23" Value="V23"/>
           <IndexField Name="NM24" Value="V24"/>
        </IndexFields>
        <Pages>
          <Page ImportFileName="P2.pdf"/>
        </Pages>
      </Document>
      <Document FormTypeName="F3">
        <IndexFields>
          <IndexField Name="NM31" Value="V31"/>
          <IndexField Name="NM32" Value="V32"/>
          <IndexField Name="NM33" Value="V33"/>
          <IndexField Name="NM34" Value="V34"/>
        </IndexFields>
        <Pages>
          <Page ImportFileName="P3.pdf"/>
        </Pages>
        </Document>
      </Documents>
    </Batch>
  </Batches>
</ImportSession>

This is my ImportSession.java

import java.io.Serializable;
import java.util.List;

import javax.xml.bind.annotation.*;

import lombok.Getter;
import lombok.Setter;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.ToString;

@XmlRootElement(name = "ImportSession")
@XmlAccessorType(XmlAccessType.FIELD)
@ToString
public class ImportSession {
    @XmlElement(name = "Batches")
    private Batches batches;
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @ToString
    public static class Batches implements Serializable {
        @XmlElement(name = "Batch")
        private Batch batch;
        
        @XmlAccessorType(XmlAccessType.FIELD)
        @ToString
        public static class Batch implements Serializable {
            @XmlElement(name = "Documents")
            private Documents documents;
            
            @XmlAccessorType(XmlAccessType.FIELD)
            @ToString
            public static class Documents implements Serializable {
                @XmlElement(name = "Document")
                private Document document;
                
                @XmlAccessorType(XmlAccessType.FIELD)
                @ToString
                public static class Document implements Serializable {
                    @XmlElement(name = "IndexFields")
                    private IndexFields indexFields;
                    @XmlElement(name = "Pages")
                    private Pages pages;
                    
                    @XmlAccessorType(XmlAccessType.FIELD)
                    @ToString
                    public static class IndexFields implements Serializable {
                        @XmlElement(name = "IndexField")
                        private List<IndexField> indexField;
                        
                        @XmlAccessorType(XmlAccessType.FIELD)
                        @ToString
                        public static class IndexField implements Serializable {
                            @XmlAttribute(name = "Name")
                            private String name;
                            
                            @XmlAttribute(name = "Value")
                            private String value;
                        }
                    }
                    
                    @XmlAccessorType(XmlAccessType.FIELD)
                    @ToString
                    public static class Pages implements Serializable {
                        @XmlElement(name = "Page")
                        private List<Page> page;
                        
                        @XmlAccessorType(XmlAccessType.FIELD)
                        @ToString
                        public static class Page implements Serializable {
                            @XmlAttribute(name = "ImportFileName")
                            private String importFileName;
                        }
                    }
                }
            }
        }
    }
}

Below is my unmarshalling code:

private static void main(String xmlFile) {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(ImportSession.class);
            
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            ImportSession ImportSession = (ImportSession) unmarshaller.unmarshal(new File(xmlFile));
            System.out.println("output = " + ImportSession);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }

In my Documents class, if I have

private Document document;

It works fine, but then it only gives me last document of my xml. But if I try to put:

private List<Document> document;

Code just comes out with nothing, no errors as well. Need help in analyzing what I am missing.

I don't want BatchFields/BatchField in my output, so I don't have that same in my class schema.

I have generated the java class ImportSession.java with xjc and got different result.

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "batches"
})
@XmlRootElement(name = "ImportSession")
public class ImportSession {

    @XmlElement(name = "Batches", required = true)
    protected ImportSession.Batches batches;

    public ImportSession.Batches getBatches() {
        return batches;
    }

    public void setBatches(ImportSession.Batches value) {
        this.batches = value;
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "batch"
    })
    public static class Batches {

        @XmlElement(name = "Batch", required = true)
        protected ImportSession.Batches.Batch batch;

        public ImportSession.Batches.Batch getBatch() {
            return batch;
        }

        public void setBatch(ImportSession.Batches.Batch value) {
            this.batch = value;
        }

        @XmlAccessorType(XmlAccessType.FIELD)
        @XmlType(name = "", propOrder = {
            "batchFields",
            "documents"
        })
        public static class Batch {

            @XmlElement(name = "BatchFields", required = true)
            protected ImportSession.Batches.Batch.BatchFields batchFields;
            @XmlElement(name = "Documents", required = true)
            protected ImportSession.Batches.Batch.Documents documents;
            @XmlAttribute(name = "BatchClassName", required = true)
            protected String batchClassName;

            public ImportSession.Batches.Batch.BatchFields getBatchFields() {
                return batchFields;
            }

            public void setBatchFields(ImportSession.Batches.Batch.BatchFields value) {
                this.batchFields = value;
            }

            public ImportSession.Batches.Batch.Documents getDocuments() {
                return documents;
            }

            public void setDocuments(ImportSession.Batches.Batch.Documents value) {
                this.documents = value;
            }

            public String getBatchClassName() {
                return batchClassName;
            }

            public void setBatchClassName(String value) {
                this.batchClassName = value;
            }

            @XmlAccessorType(XmlAccessType.FIELD)
            @XmlType(name = "", propOrder = {
                "batchField"
            })
            public static class BatchFields {

                @XmlElement(name = "BatchField", required = true)
                protected List<ImportSession.Batches.Batch.BatchFields.BatchField> batchField;

                public List<ImportSession.Batches.Batch.BatchFields.BatchField> getBatchField() {
                    if (batchField == null) {
                        batchField = new ArrayList<ImportSession.Batches.Batch.BatchFields.BatchField>();
                    }
                    return this.batchField;
                }

                @XmlAccessorType(XmlAccessType.FIELD)
                @XmlType(name = "")
                public static class BatchField {

                    @XmlAttribute(name = "Name", required = true)
                    protected String name;
                    @XmlAttribute(name = "Value", required = true)
                    protected String value;

                    public String getName() {
                        return name;
                    }

                    public void setName(String value) {
                        this.name = value;
                    }

                    public String getValue() {
                        return value;
                    }

                    public void setValue(String value) {
                        this.value = value;
                    }
                }
            }

            @XmlAccessorType(XmlAccessType.FIELD)
            @XmlType(name = "", propOrder = {
                "document"
            })
            public static class Documents {

                @XmlElement(name = "Document", required = true)
                protected List<ImportSession.Batches.Batch.Documents.Document> document;

                public List<ImportSession.Batches.Batch.Documents.Document> getDocument() {
                    if (document == null) {
                        document = new ArrayList<ImportSession.Batches.Batch.Documents.Document>();
                    }
                    return this.document;
                }

                @XmlAccessorType(XmlAccessType.FIELD)
                @XmlType(name = "", propOrder = {
                    "indexFields",
                    "pages"
                })
                public static class Document {

                    @XmlElement(name = "IndexFields", required = true)
                    protected ImportSession.Batches.Batch.Documents.Document.IndexFields indexFields;
                    @XmlElement(name = "Pages", required = true)
                    protected ImportSession.Batches.Batch.Documents.Document.Pages pages;
                    @XmlAttribute(name = "FormTypeName", required = true)
                    protected String formTypeName;

                    public ImportSession.Batches.Batch.Documents.Document.IndexFields getIndexFields() {
                        return indexFields;
                    }

                    public void setIndexFields(ImportSession.Batches.Batch.Documents.Document.IndexFields value) {
                        this.indexFields = value;
                    }

                    public ImportSession.Batches.Batch.Documents.Document.Pages getPages() {
                        return pages;
                    }

                    public void setPages(ImportSession.Batches.Batch.Documents.Document.Pages value) {
                        this.pages = value;
                    }

                    public String getFormTypeName() {
                        return formTypeName;
                    }

                    public void setFormTypeName(String value) {
                        this.formTypeName = value;
                    }

                    @XmlAccessorType(XmlAccessType.FIELD)
                    @XmlType(name = "", propOrder = {
                        "indexField"
                    })
                    public static class IndexFields {

                        @XmlElement(name = "IndexField", required = true)
                        protected List<ImportSession.Batches.Batch.Documents.Document.IndexFields.IndexField> indexField;

                        public List<ImportSession.Batches.Batch.Documents.Document.IndexFields.IndexField> getIndexField() {
                            if (indexField == null) {
                                indexField = new ArrayList<ImportSession.Batches.Batch.Documents.Document.IndexFields.IndexField>();
                            }
                            return this.indexField;
                        }

                        @XmlAccessorType(XmlAccessType.FIELD)
                        @XmlType(name = "")
                        public static class IndexField {

                            @XmlAttribute(name = "Name", required = true)
                            protected String name;
                            @XmlAttribute(name = "Value", required = true)
                            protected String value;

                            public String getName() {
                                return name;
                            }

                            public void setName(String value) {
                                this.name = value;
                            }

                            public String getValue() {
                                return value;
                            }

                            public void setValue(String value) {
                                this.value = value;
                            }
                        }
                    }

                    @XmlAccessorType(XmlAccessType.FIELD)
                    @XmlType(name = "", propOrder = {
                        "page"
                    })
                    public static class Pages {

                        @XmlElement(name = "Page", required = true)
                        protected ImportSession.Batches.Batch.Documents.Document.Pages.Page page;

                        public ImportSession.Batches.Batch.Documents.Document.Pages.Page getPage() {
                            return page;
                        }

                        public void setPage(ImportSession.Batches.Batch.Documents.Document.Pages.Page value) {
                            this.page = value;
                        }

                        @XmlAccessorType(XmlAccessType.FIELD)
                        @XmlType(name = "")
                        public static class Page {

                            @XmlAttribute(name = "ImportFileName", required = true)
                            protected String importFileName;

                            public String getImportFileName() {
                                return importFileName;
                            }

                            public void setImportFileName(String value) {
                                this.importFileName = value;
                            }
                        }
                    }
                }
            }
        }
    }
}

I have first generated the xsd file from your xml file with this online site:https://www.liquid-technologies.com/online-xml-to-xsd-converter
Generated xsd (I name it "test.xsd"):

<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Technologies Online Tools 1.0 (https://www.liquid-technologies.com) -->
<xs:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="ImportSession">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Batches">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Batch">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="BatchFields">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element maxOccurs="unbounded" name="BatchField">
                            <xs:complexType>
                              <xs:attribute name="Name" type="xs:string" use="required" />
                              <xs:attribute name="Value" type="xs:string" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                    <xs:element name="Documents">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element maxOccurs="unbounded" name="Document">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="IndexFields">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element maxOccurs="unbounded" name="IndexField">
                                        <xs:complexType>
                                          <xs:attribute name="Name" type="xs:string" use="required" />
                                          <xs:attribute name="Value" type="xs:string" use="required" />
                                        </xs:complexType>
                                      </xs:element>
                                    </xs:sequence>
                                  </xs:complexType>
                                </xs:element>
                                <xs:element name="Pages">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element name="Page">
                                        <xs:complexType>
                                          <xs:attribute name="ImportFileName" type="xs:string" use="required" />
                                        </xs:complexType>
                                      </xs:element>
                                    </xs:sequence>
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                              <xs:attribute name="FormTypeName" type="xs:string" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name="BatchClassName" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Generation of ImportSession.java ( xjc is included in JDK8):

$ xjc test.xsd 
analyse dun schéma...
compilation dun schéma...
generated/ImportSession.java
generated/ObjectFactory.java

I have remove the first line package generated; and many many auto-generated comments.

Now,
System.out.println("output = " + ImportSession.getBatches().getBatch().getDocuments().getDocument().size());
show 3 .

Your <Documents> XML element contains several <Document> elements.

Therefore in your Documents Java class you don't need a member of type Document , but instead of type List<Document> . So in your Documents class you need to replace

@XmlElement(name = "Document")
private Document document;

by

@XmlElement(name = "Document")
private List<Document> documents;

I also changed the member name from document to documents , because using a plural word is common practice for lists.

While the above modification already fixes your problem, there is even more room for improvement:

Currently you have two separate classes Documents and Document for modeling XML content like:

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

You can simplify the Java code by using the @XmlElementWrapper annotation. In your Batch class you can replace

@XmlElement(name = "Documents")
private Documents documents;

by

@XmlElementWrapper(name = "Documents")
@XmlElement(name = "Document")
private List<Document> documents;

By doing so you don't need the Documents class anymore.


You might want to do similar modifications for the other lists in your Java code corresponding to

  • <Batches> containing several <Batch> elements
  • <BatchFields> containing several <BatchField> elements
  • <IndexFields> containing several <IndexField> elements
  • <Pages> containing several <Page> elements

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM