Mapping XMLs with different schemas to the same classes using JAXB - java

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.

Related

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

How to create pojo with same xml tag in child and parent [duplicate]

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.

Is it possible to refer to an existing class as a base in .xsd file jaxb

I'm starting learning JAXB, so this question can be very silly. Now I have two classes, a base "base.java" and a derived class "child.java". These classes were generated using a ".xsd" file. I have another class "secondBase.java", this class was not generated by my ".xsd". My question is:
Is it possible to use "secondBase.java" as a base for "child.java" instead of the base created by my Jaxb ?
Here's my .xsd file :
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb" jxb:version="2.1">
<xs:complexType name="base">
<xs:sequence>
<xs:element name="id" type="xs:string" minOccurs="1"
maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:element name="chlid" />
<xs:complexType name="chlid">
<xs:complexContent>
<xs:extension base="base">
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
My secondBase.java :
public class secondBase {
public secondBase(){
id="0";
}
protected String id;
public String getId() {
return id;
}
public void setId(String value) {
this.id = value;
}
}
My base.java (generated using .xsd file)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "base", propOrder = {
"id"
})
#XmlSeeAlso({
Chlid.class
})
public class Base {
#XmlElement(required = true)
protected String id;
public String getId() {
return id;
}
public void setId(String value) {
this.id = value;
}
}
My child.java (generated using .xsd file)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "chlid")
public class Chlid
extends Base
{
}
Thank's in advance :)
Here's the solution, it might be helpfull for someone .. I added this to my .xsd file :
<xs:complexType name="secondBase">
<xs:annotation>
<xs:appinfo>
<jaxb:class name="secondBase" implClass="com.myjaxb.classes.secondBase"/>
</xs:appinfo>
</xs:annotation>
</xs:complexType>
And i Used this "complextype as an extention to my element :)
PS : this answer is inspired from this topic : how to force schema compiled classes to extend specific class outside schema

How to use Numbers and whitespace in xmlID xmlIDREF JAXB

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>

JAXB generated classes for fixed attribute in subtype

My java classes are being generated from xsd file.
The goal I want to accomplish is to have some "known" properties based on the type of elements. For instance, I have a list of animals. After the xml is being parsed, I want to know in the code how many legs have my animals. But the number of legs is a characteristic of the animal type, so, in case I have a cat it will have 4 legs and the kangaroo will have 2 legs.
In case I define the xsd like this:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="Animals" type="animals" />
<xs:complexType name="animals">
<xs:sequence>
<xs:element name="Animal" maxOccurs="unbounded" type="animal"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="animal" abstract="true">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="catType">
<xs:simpleContent>
<xs:extension base="animal">
<xs:attribute name="nbOfLegs" type="xs:integer" fixed="4" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="kangarooType">
<xs:simpleContent>
<xs:extension base="animal">
<xs:attribute name="nbOfLegs" type="xs:integer" fixed="2" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
The generated classes are as expected (I removed the annotations):
public abstract class Animal {
protected String name;
public String getName() {return name;}
public void setName(String value) {this.name = value;}
}
public class CatType extends Animal {
protected BigInteger nbOfLegs;
public BigInteger getNbOfLegs() {
if (nbOfLegs == null) {
return new BigInteger("4");
} else {
return nbOfLegs;
}
}
public void setNbOfLegs(BigInteger value) {this.nbOfLegs = value;}
}
This way, in case the user sets the number of legs for cats in the xml, it can only be 4, and if he doesn't, in code I will receive 4 anyway. Similar for kangaroo I always receive 2 legs.
But the problem with this approach is that I cannot use polymoprhism like this:
for(Animal animal : animals) {
System.out.println(animal.getNbOfLegs());
}
So I tried a different approach:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="Animals" type="animals" />
<xs:complexType name="animals">
<xs:sequence>
<xs:element name="Animal" maxOccurs="unbounded" type="animal"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="animal" abstract="true">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" />
<xs:attribute name="nbOfLegs" type="xs:integer"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="catType">
<xs:simpleContent>
<xs:restriction base="animal">
<xs:attribute name="nbOfLegs" type="xs:integer" fixed="4" />
</xs:restriction>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="kangarooType">
<xs:simpleContent>
<xs:restriction base="animal">
<xs:attribute name="nbOfLegs" type="xs:integer" fixed="2" />
</xs:restriction>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
The Animal class is generated as expected:
public abstract class Animal {
protected String name;
protected BigInteger nbOfLegs;
public String getName() {return name;}
public void setName(String value) {this.name = value;}
public BigInteger getNbOfLegs() {return nbOfLegs;}
public void setNbOfLegs(BigInteger value) {this.nbOfLegs = value;}
}
But the generated CatType is empty.
I expected it to be like this:
public class CatType extends Animal {
#Override
public BigInteger getNbOfLegs() {
if (nbOfLegs == null) {
return new BigInteger("4");
} else {
return nbOfLegs;
}
}
}
Is it possible to customize the bindings file in order to achieve the desired generated CatType class?
Thank you.

Categories

Resources