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
Related
This question already has answers here:
JaxB rename class with duplicate name
(1 answer)
how to rename nested classes in jaxb xjc
(2 answers)
Customize object/element name with JAXB
(2 answers)
Closed 2 years ago.
I am having an xml which has xml tags (states) same as that of parent and child. So while creating the pojos using the jaxb xjc command, I am getting 2 static inner classes with same name. I tried using bindings.xml but my problem didnt resolve and instead I am not getting error. Please find all the details below
XML:
<universe>
<cotest>
<test>
<country>
<states>
<requirement type="op">
<states>
<cashitem>
<currency>usd</currency>
<val>o</val>
</cashitem>
</states>
</requirement>
</states>
</country>
</test>
</cotest>
</universe>
This is XSD thats I generated online
<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Technologies Online Tools 1.0 (https://www.liquid-technologies.com) -->
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="universe">
<xs:complexType>
<xs:sequence>
<xs:element name="cotest">
<xs:complexType>
<xs:sequence>
<xs:element name="test">
<xs:complexType>
<xs:sequence>
<xs:element name="country">
<xs:complexType>
<xs:sequence>
<xs:element name="states">
<xs:complexType>
<xs:sequence>
<xs:element name="requirement">
<xs:complexType>
<xs:sequence>
<xs:element name="states">
<xs:complexType>
<xs:sequence>
<xs:element name="cashitem">
<xs:complexType>
<xs:sequence>
<xs:element name="currency" type="xs:string" />
<xs:element name="val" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="type" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
bindings.xml
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jaxb:bindings>
<jaxb:globalBindings generateElementProperty="false" fixedAttributeAsConstantProperty="true" choiceContentProperty="true" localScoping="toplevel"/>
</jaxb:bindings>
<jaxb:bindings schemaLocation="abc.xsd" node="/xs:schema">
<jaxb:bindings node="//xs:schema//xs:element[#name='universe']//xs:sequence//xs:element[#name='states']//xs:complexType//xs:sequence//xs:element[#name='states']">
<jaxb:class name="object2" />
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
I am executing xjc using the command xjc -b bindings.xml -d photos -p com.test1 abc.xsd
error:
parsing a schema...
compiling a schema...
[ERROR] A class/interface with the same name "com.test1.States" is already in us
e. Use a class customization to resolve this conflict.
line 17 of file:/D:/user/abc.xsd
[ERROR] (Relevant to above error) another "States" is generated from here.
line 23 of file:/D:/user/abc.xsd
[ERROR] Two declarations cause a collision in the ObjectFactory class.
line 17 of file:/D:/user/abc.xsd
[ERROR] (Related to above error) This is the other declaration.
line 23 of file:/D:/user/abc.xsd
Failed to produce code.
If I dont use bindings.xml then i can see classes are generated but have duplicate inner classes and hence my eclipse IDE shows compile time error
command: xjc -d photos -p com.test1 abc.xsd
package com.test1;
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 = {
"cotest"
})
#XmlRootElement(name = "universe")
public class Universe {
#XmlElement(required = true)
protected Universe.Cotest cotest;
public Universe.Cotest getCotest() {
return cotest;
}
public void setCotest(Universe.Cotest value) {
this.cotest = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"test"
})
public static class Cotest {
#XmlElement(required = true)
protected Universe.Cotest.Test test;
public Universe.Cotest.Test getTest() {
return test;
}
public void setTest(Universe.Cotest.Test value) {
this.test = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"country"
})
public static class Test {
#XmlElement(required = true)
protected Universe.Cotest.Test.Country country;
public Universe.Cotest.Test.Country getCountry() {
return country;
}
public void setCountry(Universe.Cotest.Test.Country value) {
this.country = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"states"
})
public static class Country {
#XmlElement(required = true)
protected Universe.Cotest.Test.Country.States states;
public Universe.Cotest.Test.Country.States getStates() {
return states;
}
public void setStates(Universe.Cotest.Test.Country.States value) {
this.states = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"requirement"
})
public static class States {
#XmlElement(required = true)
protected Universe.Cotest.Test.Country.States.Requirement requirement;
public Universe.Cotest.Test.Country.States.Requirement getRequirement() {
return requirement;
}
public void setRequirement(Universe.Cotest.Test.Country.States.Requirement value) {
this.requirement = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"states"
})
public static class Requirement {
#XmlElement(required = true)
protected Universe.Cotest.Test.Country.States.Requirement.States states;
#XmlAttribute(name = "type", required = true)
protected String type;
public Universe.Cotest.Test.Country.States.Requirement.States getStates() {
return states;
}
public void setStates(Universe.Cotest.Test.Country.States.Requirement.States value) {
this.states = value;
}
public String getType() {
return type;
}
public void setType(String value) {
this.type = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"cashitem"
})
public static class States {
#XmlElement(required = true)
protected Universe.Cotest.Test.Country.States.Requirement.States.Cashitem cashitem;
public Universe.Cotest.Test.Country.States.Requirement.States.Cashitem getCashitem() {
return cashitem;
}
public void setCashitem(Universe.Cotest.Test.Country.States.Requirement.States.Cashitem value) {
this.cashitem = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"currency",
"val"
})
public static class Cashitem {
#XmlElement(required = true)
protected String currency;
#XmlElement(required = true)
protected String val;
public String getCurrency() {
return currency;
}
public void setCurrency(String value) {
this.currency = value;
}
public String getVal() {
return val;
}
public void setVal(String value) {
this.val = value;
}
}
}
}
}
}
}
}
}
and this is objectfactory
package com.test1;
import javax.xml.bind.annotation.XmlRegistry;
#XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public Universe createUniverse() {
return new Universe();
}
public Universe.Cotest createUniverseCotest() {
return new Universe.Cotest();
}
public Universe.Cotest.Test createUniverseCotestTest() {
return new Universe.Cotest.Test();
}
public Universe.Cotest.Test.Country createUniverseCotestTestCountry() {
return new Universe.Cotest.Test.Country();
}
public Universe.Cotest.Test.Country.States createUniverseCotestTestCountryStates() {
return new Universe.Cotest.Test.Country.States();
}
public Universe.Cotest.Test.Country.States.Requirement createUniverseCotestTestCountryStatesRequirement() {
return new Universe.Cotest.Test.Country.States.Requirement();
}
public Universe.Cotest.Test.Country.States.Requirement.States createUniverseCotestTestCountryStatesRequirementStates() {
return new Universe.Cotest.Test.Country.States.Requirement.States();
}
public Universe.Cotest.Test.Country.States.Requirement.States.Cashitem createUniverseCotestTestCountryStatesRequirementStatesCashitem() {
return new Universe.Cotest.Test.Country.States.Requirement.States.Cashitem();
}
}
As you can see from the universe classs, there are 2 static inner classes public static class States and this is the issue.
Can you any please provide a solution?
Let me know if any more information required.
I have these two types of XMLs with no predefined schemas:
A
<root-a>
<a-item id="a1">
<name>Name of A1</name>
<a-item id="a11">
<name>Name of A11</name>
</a-item>
<a-item id="a12">
<name>Name of A12</name>
<a-item id="a121">
<name>Name of A121</name>
</a-item>
<a-item id="a122">
<name>Name of A122</name>
</a-item>
</a-item>
</a-item>
<a-item id="a2">
<name>Name of A2</name>
</a-item>
</root-a>
B
<root-b>
<b-item id="b1">
<name>Name of B1</name>
<b-item id="b11">
<name>Name of B11</name>
</b-item>
<!-- etc., similar to A -->
</b-item>
</root-b>
The items can be nested to an arbitrary depth. The structure is the same, but the name of the root element and of the item elements is different. How can I map it to a single Java class structure, eg. like this one (getters and setters omitted) using JAXB:
public class Root {
private List<Item> items;
}
public class Item {
private String id;
private String name;
private List<Item> items;
}
I can map just one of the XML structures using JAXB annotations, but I don't know how to do it to accommodate both XMLs at the same time. I could create a parallel hierarchy for A and B with a common interface, but I hope there's a neater solution.
Here is XML schema based on your description:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<!-- Root elements -->
<xs:element name="root-a" type="rootAType"/>
<xs:element name="root-b" type="rootBType"/>
<!-- root-a type -->
<xs:complexType name="rootAType">
<xs:sequence>
<xs:element name="a-item" type="itemAType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- root-b type -->
<xs:complexType name="rootBType">
<xs:sequence>
<xs:element name="b-item" type="itemBType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- abstract item type -->
<xs:complexType name="itemType" abstract="true">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
<!-- item-a type -->
<xs:complexType name="itemAType">
<xs:complexContent>
<xs:extension base="itemType">
<xs:sequence>
<xs:element name="a-item" type="itemAType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<!-- item-b type -->
<xs:complexType name="itemBType">
<xs:complexContent>
<xs:extension base="itemType">
<xs:sequence>
<xs:element name="b-item" type="itemBType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
On base of this schema following classes were created:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "itemType", propOrder = {
"name"
})
#XmlSeeAlso({
ItemAType.class,
ItemBType.class
})
public abstract class ItemType {
#XmlElement(required = true)
protected String name;
#XmlAttribute(name = "id")
protected String id;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getId() {
return id;
}
public void setId(String value) {
this.id = value;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "itemAType", propOrder = {
"aItem"
})
public class ItemAType
extends ItemType
{
#XmlElement(name = "a-item")
protected List<ItemAType> aItem;
public List<ItemAType> getAItem() {
if (aItem == null) {
aItem = new ArrayList<ItemAType>();
}
return this.aItem;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "itemBType", propOrder = {
"bItem"
})
public class ItemBType
extends ItemType
{
#XmlElement(name = "b-item")
protected List<ItemBType> bItem;
public List<ItemBType> getBItem() {
if (bItem == null) {
bItem = new ArrayList<ItemBType>();
}
return this.bItem;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "rootAType", propOrder = {
"aItem"
})
public class RootAType {
#XmlElement(name = "a-item")
protected List<ItemAType> aItem;
public List<ItemAType> getAItem() {
if (aItem == null) {
aItem = new ArrayList<ItemAType>();
}
return this.aItem;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "rootBType", propOrder = {
"bItem"
})
public class RootBType {
#XmlElement(name = "b-item")
protected List<ItemBType> bItem;
public List<ItemBType> getBItem() {
if (bItem == null) {
bItem = new ArrayList<ItemBType>();
}
return this.bItem;
}
}
That seems to be a bit too complicated, but if you have more common functionality between a-item and b-item, the structure will become more convenient.
I have the following classes:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
#XmlAttribute
#XmlID
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Employee getManager() {
return manager;
}
public void setManager(Employee manager) {
this.manager = manager;
}
public List<Employee> getReports() {
return reports;
}
public void setReports(List<Employee> reports) {
this.reports = reports;
}
#XmlAttribute
private String name;
#XmlIDREF
private Employee manager;
#XmlElement(name="report")
#XmlIDREF
private List<Employee> reports;
public Employee() {
reports = new ArrayList<Employee>();
}
}
The Company class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Company {
#XmlElement(name="employee")
private List<Employee> employees;
public Company() {
employees = new ArrayList<Employee>();
}
public List<Employee> getEmployees(){
return employees;
}
}
My main contains the following:
Employee employee1 = new Employee();
employee1.setId("1");
employee1.setName("Jane Doe");
company.getEmployees().add(employee1);
Employee employee2 = new Employee();
employee2.setId("2");
employee2.setName("John Smith");
employee2.setManager(employee1);
employee1.getReports().add(employee2);
company.getEmployees().add(employee2);
Employee employee3 = new Employee();
employee3.setId("3");
employee3.setName("Anne Jones");
employee3.setManager(employee1);
employee1.getReports().add(employee3);
company.getEmployees().add(employee3);
If I marshall this it runs fine. But if I create the following schema and then add the schema in the main then I get the following errors:
Schema:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="company" type="company"/>
<xs:complexType name="company">
<xs:sequence>
<xs:element name="employee" type="employee" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="employee">
<xs:sequence>
<xs:element name="manager" type="xs:IDREF" minOccurs="0"/>
<xs:element name="report" type="xs:IDREF" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:ID"/>
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
</xs:schema>
Error Received:
Exception in thread "main" javax.xml.bind.MarshalException
- with linked exception:
[org.xml.sax.SAXParseException: cvc-datatype-valid.1.2.1: '1' is not a valid value for 'NCName'.]
I am aware this is because NCName cannot start with a number and have whitespaces. But in my case the id is a number.
I want to know:
1) Why does this only happen when I include the schema?
2) What is the workaround, and is the only way to use moxy
1) Setting an XML Schema using marshaller.setSchema(schema); turns on validation against that XML Schema, and you know that an all-digit ID value is invalid.
2) You can use an Adapter.
Add an annotation in Employee.java:
#XmlAttribute
#XmlID
#XmlJavaTypeAdapter(Adapter.class)
private String id;
And you'll need this simple class:
public class Adapter extends XmlAdapter<String, String> {
#Override
public String marshal(String s) throws Exception {
return "_" + s;
}
#Override
public String unmarshal(String v) throws Exception {
return v.substring(1);
}
}
The resulting XML will be correct according to XML Schema:
<company>
<employee id="_1" name="Jane Doe">
<report>_2</report>
<report>_3</report>
</employee>
<employee id="_2" name="John Smith">
<manager>_1</manager>
</employee>
<employee id="_3" name="Anne Jones">
<manager>_1</manager>
</employee>
</company>
I want to read or write multi-level Xml file via Jaxb (cannot use xml parser).
For example one part(considered as parent part) has many child part. That child part also has child parts.
So the structure is like this which I have created manually.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<part xmlns="http://www.serus.com">
<part>
<Part_Number>n0</Part_Number>
<Part_Version>revision0</Part_Version>
<Part_Type>p0</Part_Type>
<Status>current0</Status>
</part>
<part>
<Part_Number>n1</Part_Number>
<Part_Version>revision1</Part_Version>
<Part_Type>p1</Part_Type>
<Status>current1</Status>
<part>
<Part_Number>n2</Part_Number>
<Part_Version>revision2</Part_Version>
<Part_Type>p2</Part_Type>
<Status>current2</Status>
<part>
<Part_Number>n3</Part_Number>
<Part_Version>revision3</Part_Version>
<Part_Type>p3</Part_Type>
<Status>current3</Status>
</part>
</part>
<part>
<Part_Number>n3</Part_Number>
<Part_Version>revision3</Part_Version>
<Part_Type>p3</Part_Type>
<Status>current3</Status>
</part>
</part>
<part>
<Part_Number>n4</Part_Number>
<Part_Version>revision4</Part_Version>
<Part_Type>p4</Part_Type>
<Status>current4</Status>
</part>
</part>
I have to perform marshalling & Unmarshalling for such kind of data. This is the project requirement. As of my knowledge, in jaxb I have set of classes. I have to created some methods, so that I can perform this via java program.
Please help me. I will appreaciate for your suggestions & answers.
Your Java model is going to look something like the following. A Part class that holds onto a list of other Part instances as well as some other data.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Part {
#XmlElement(name="Part_Number");
private String partNo;
#XmlElement(name="part")
private List<Part> parts;
}
You may find the following articles from my blog helpful:
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html
Namespace Qualification
Since your XML document is namespace qualified you are going to need to factor this into your mapping metadata. I would recommend doing this with the package level #XmlSchema annotation.
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
I presume there was a copy error in your XML. Either your root element is <partlist> or it is missing the elements Part_Number, Part_Version, Part_Type and Status (that would make sense). So your schema would look like this:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.serus.com" xmlns:ns1="http://www.serus.com">
<xs:element name="part">
<xs:complexType>
<xs:sequence minOccurs="1" maxOccurs="1">
<xs:element ref="ns1:Part_Number"/>
<xs:element ref="ns1:Part_Type"/>
<xs:element ref="ns1:Part_Version"/>
<xs:element ref="ns1:Status"/>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="ns1:part"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Part_Number" type="xs:NCName"/>
<xs:element name="Part_Type" type="xs:NCName"/>
<xs:element name="Part_Version" type="xs:NCName"/>
<xs:element name="Status" type="xs:NCName"/>
</xs:schema>
Then a class would have this format:
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlRootElement(name = "part")
public class Part implements Serializable {
public static Part fromXML(InputStream in) throws Exception {
JAXBContext context = JAXBContext.newInstance(Part.class);
Unmarshaller um = context.createUnmarshaller();
return (Part) um.unmarshal(in);
}
private static final long serialVersionUID = 1L;
#XmlElement(name = "Part_Number")
private String partNumber;
#XmlElement(name = "Part_Type")
private String partType;
#XmlElement(name = "Part_Version")
private String partVersion;
#XmlElement(name = "Status")
private String status;
#XmlElement(name = "part")
private Collection<Part> subParts = null;
public static void main(String args[]) throws JAXBException {
Part mainPart = new Part("1m", "mType", "version 1", "draft");
Part level1 = new Part("l1", "l1Type", "version 11", "new");
level1.addPart(new Part("l2", "l2Type", "version 1", "new"));
level1.addPart(new Part("l2-2", "l2Type", "version 1", "new"));
mainPart.addPart(level1);
System.out.println(mainPart.toXML());
}
public Part() {
// No action here
}
public Part(String no, String type, String ver, String stat) {
this.partNumber = no;
this.partType = type;
this.partVersion = ver;
this.status = stat;
}
#XmlTransient
public String getPartNumber() {
return this.partNumber;
}
public void setPartNumber(String partNumber) {
this.partNumber = partNumber;
}
#XmlTransient
public String getPartType() {
return this.partType;
}
public void setPartType(String partType) {
this.partType = partType;
}
#XmlTransient
public String getPartVersion() {
return this.partVersion;
}
public void setPartVersion(String partVersion) {
this.partVersion = partVersion;
}
#XmlTransient
public String getStatus() {
return this.status;
}
public void setStatus(String status) {
this.status = status;
}
#XmlTransient
public Collection<Part> getSubparts() {
return this.subParts;
}
public void setSubparts(Collection<Part> subparts) {
this.subParts = subparts;
}
public void addPart(Part part) {
if (this.subParts == null) {
this.subParts = new ArrayList<Part>();
}
this.subParts.add(part);
}
public void addParts(Collection<Part> parts) {
if (this.subParts == null) {
this.subParts = new ArrayList<Part>();
}
this.subParts.addAll(parts);
}
public String toXML() throws JAXBException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
JAXBContext context = JAXBContext.newInstance(Part.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(this, out);
return out.toString();
}
}
The trick here is the strategic use of #XmlElement(name = "part") and #XmlTransient. That should do the trick
You need not even create the Java obejcts manually, if you have the xsd for your xml. Check this one out -
How to generate JAXB classes from XSD?.
Marshalling and unmarshalling - try out http://www.mkyong.com/java/jaxb-hello-world-example/.
You should really try out something and then ask questions if you are stuck at some point. That would make the responses very specific to the problem.
I try to get validation message in variable with Jaxb.
Try example from here http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/api/javax/xml/bind/Unmarshaller.html
My code:
JAXBContext jaxbContext = JAXBContext.newInstance("com.piyush");
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new StreamSource(new File("D:/liferay-develop/workspace/cat_test/v1/STD_MP.xsd")));
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setSchema(schema);
ValidationEventCollector validationCollector= new ValidationEventCollector();
jaxbUnmarshaller.setEventHandler( validationCollector );
STDMP ts = (STDMP)jaxbUnmarshaller.unmarshal(xml_gkuzu);
if(validationCollector.hasEvents())
{
for(ValidationEvent event:validationCollector.getEvents())
{
String msg = event.getMessage();
System.out.println(msg);
}
}
But nothing happens. What am I doing wrong ?
The following should help:
JAXB2ValidationEventCollector
ValidationEventCollector came from JAXB 1 (JSR-31) and doesn't appear to support the changes we made to validation in JAXB 2 (JSR-222) very well. You can solve this issue by creating a subclass of ValidationEventHandler like the following.
package forum12295028;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.util.ValidationEventCollector;
class JAXB2ValidationEventCollector extends ValidationEventCollector {
#Override
public boolean handleEvent(ValidationEvent event) {
super.handleEvent(event);
return true;
}
}
EXAMPLE
The following example can be used to prove that everything works
Customer
package forum12295028;
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Customer {
private String name;
private List<PhoneNumber> phoneNumbers =
new ArrayList<PhoneNumber>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement(name="phone-number")
public List<PhoneNumber> getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
}
PhoneNumber
package forum12295028;
public class PhoneNumber {
}
customer.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="stringMaxSize5"/>
<xs:element ref="phone-number" maxOccurs="2"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="phone-number">
<xs:complexType>
<xs:sequence/>
</xs:complexType>
</xs:element>
<xs:simpleType name="stringMaxSize5">
<xs:restriction base="xs:string">
<xs:maxLength value="5"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<name>Jane Doe</name>
<phone-number/>
<phone-number/>
<phone-number/>
</customer>
Demo
package forum12295028;
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.bind.*;
import javax.xml.bind.util.ValidationEventCollector;
import javax.xml.validation.*;
public class Demo {
public static void main(String[] args) throws Exception {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File("src/forum12295028/customer.xsd"));
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema(schema);
ValidationEventCollector validationCollector = new JAXB2ValidationEventCollector();
unmarshaller.setEventHandler(validationCollector);
Customer customer = (Customer) unmarshaller.unmarshal(new File("src/forum12295028/input.xml"));
if(validationCollector.hasEvents())
{
for(ValidationEvent event:validationCollector.getEvents())
{
String msg = event.getMessage();
System.out.println(msg);
}
}
}
}
Output
cvc-maxLength-valid: Value 'Jane Doe' with length = '8' is not facet-valid with respect to maxLength '5' for type 'stringMaxSize5'.
cvc-type.3.1.3: The value 'Jane Doe' of element 'name' is not valid.
cvc-complex-type.2.4.d: Invalid content was found starting with element 'phone-number'. No child element is expected at this point.