I have an XML like the following.
<Transactions>
<FileHeader>
<!--SOME VALUES-->
</FileHeader>
<BatchHeader>
<!--SOME VALUES-->
</BatchHeader>
<Entry>
<!--SOME VALUES-->
</Entry>
<Addenda>
<!--SOME VALUES-->
</Addenda>
<Entry>
<!--SOME VALUES-->
</Entry>
<Addenda>
<!--SOME VALUES-->
</Addenda>
<BatchControl>
<!--SOME VALUES-->
</BatchControl>
<BatchHeader>
<!--SOME VALUES-->
</BatchHeader>
<Entry>
<!--SOME VALUES-->
</Entry>
<Addenda>
<!--SOME VALUES-->
</Addenda>
<BatchControl>
<!--SOME VALUES-->
</BatchControl>
<FileControl>
<!--SOME VALUES-->
</FileControl>
</Transactions>
Essentially, except FileHeader and FileControl all are lists. I am parsing (unmarshalling) the xml using JAXB, with the following defined objects:
@Getter @Setter
@XmlRootElement(name = "Transactions")
@XmlAccessorType(XmlAccessType.FIELD)
public class Transactions {
@XmlElement(name = "FileHeader")
private FileHeader fileHeader;
@XmlElement(name = "BatchHeader")
private List<BatchHeader> batchHeaderList;
@XmlElement(name = "Entry")
private List<Entry> entryList;
@XmlElement(name = "Addenda")
private List<Addenda> addendaList;
@XmlElement(name = "BatchControl")
private List<BatchControl> batchControl;
@XmlElement(name = "FileControl")
private FileControl fileControl;
}
Now the marshalling works well. The order of the tags are maintained, that is the BatchHeader at index x
corresponds with BatchControl at index x
.
But while unmarshalling all the BatchHeader comes first, then all Entries, then Addendum and so on. With my experience so far with JAXB, I understand this is the logical behavior however, not my desired behavior. I want the xml to be in exact order as it was originally, that is BatchHeader 1, then the Entries and Addedum for it, then BatchControl 1, then BatchHeader 2 and so on.
I tried with @XmlType(propOrder={})
but realized it's not for my exact purpose. I looked up in the internet but did not find much. Online LLM models were no help whatsoever.
Is is possible with JAXB ? and If so how ? Currently I have resorted to the manual approach of painfully adding each data to a StringBuilder and then creating the xml.
I have an XML like the following.
<Transactions>
<FileHeader>
<!--SOME VALUES-->
</FileHeader>
<BatchHeader>
<!--SOME VALUES-->
</BatchHeader>
<Entry>
<!--SOME VALUES-->
</Entry>
<Addenda>
<!--SOME VALUES-->
</Addenda>
<Entry>
<!--SOME VALUES-->
</Entry>
<Addenda>
<!--SOME VALUES-->
</Addenda>
<BatchControl>
<!--SOME VALUES-->
</BatchControl>
<BatchHeader>
<!--SOME VALUES-->
</BatchHeader>
<Entry>
<!--SOME VALUES-->
</Entry>
<Addenda>
<!--SOME VALUES-->
</Addenda>
<BatchControl>
<!--SOME VALUES-->
</BatchControl>
<FileControl>
<!--SOME VALUES-->
</FileControl>
</Transactions>
Essentially, except FileHeader and FileControl all are lists. I am parsing (unmarshalling) the xml using JAXB, with the following defined objects:
@Getter @Setter
@XmlRootElement(name = "Transactions")
@XmlAccessorType(XmlAccessType.FIELD)
public class Transactions {
@XmlElement(name = "FileHeader")
private FileHeader fileHeader;
@XmlElement(name = "BatchHeader")
private List<BatchHeader> batchHeaderList;
@XmlElement(name = "Entry")
private List<Entry> entryList;
@XmlElement(name = "Addenda")
private List<Addenda> addendaList;
@XmlElement(name = "BatchControl")
private List<BatchControl> batchControl;
@XmlElement(name = "FileControl")
private FileControl fileControl;
}
Now the marshalling works well. The order of the tags are maintained, that is the BatchHeader at index x
corresponds with BatchControl at index x
.
But while unmarshalling all the BatchHeader comes first, then all Entries, then Addendum and so on. With my experience so far with JAXB, I understand this is the logical behavior however, not my desired behavior. I want the xml to be in exact order as it was originally, that is BatchHeader 1, then the Entries and Addedum for it, then BatchControl 1, then BatchHeader 2 and so on.
I tried with @XmlType(propOrder={})
but realized it's not for my exact purpose. I looked up in the internet but did not find much. Online LLM models were no help whatsoever.
Is is possible with JAXB ? and If so how ? Currently I have resorted to the manual approach of painfully adding each data to a StringBuilder and then creating the xml.
Share Improve this question edited yesterday ShahriyarHossain asked 2 days ago ShahriyarHossainShahriyarHossain 801 silver badge8 bronze badges 1- Marshaling is probably following the order within the list so a LinkedHashSet would be advisable. – LMC Commented 2 days ago
1 Answer
Reset to default 0I have not tested this but here's a design that groups the related quartet (Addenda, BatchControl, BatchHeader, Entry) into a choice
element. The choice
element allows for a heterogeneous list of sub-elements. The order in the choice
does not matter (to JAXB); but, I believe JAXB will honor the order of the elements during unmarshaling and again during marshaling.
Transactions.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3./2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="Transactions">
<xs:complexType>
<xs:sequence>
<xs:element ref="FileHeader"/>
<xs:choice maxOccurs="unbounded">
<xs:element ref="Addenda"/>
<xs:element ref="BatchControl"/>
<xs:element ref="BatchHeader"/>
<xs:element ref="Entry"/>
</xs:choice>
<xs:element ref="FileControl"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="FileHeader">
<xs:complexType/>
</xs:element>
<xs:element name="Addenda">
<xs:complexType/>
</xs:element>
<xs:element name="BatchControl">
<xs:complexType/>
</xs:element>
<xs:element name="BatchHeader">
<xs:complexType/>
</xs:element>
<xs:element name="Entry">
<xs:complexType/>
</xs:element>
<xs:element name="FileControl">
<xs:complexType/>
</xs:element>
</xs:schema>
Using XJC to generate the Java classes produces this set:
XJC Generated Classes
Addenda.java
BatchControl.java
BatchHeader.java
Entry.java
FileControl.java
FileHeader.java
ObjectFactory.java
Transactions.java
For the simple XML example given, only the Transactions
class is interesting. It declares a propOrder
for fileHeader
, addendaOrBatchControlOrBatchHeader
, and fileControl
to keep the general ordering in line. The addendaOrBatchControlOrBatchHeader
represents the choice
structure and it holds the unmarshalled elements from the quartet in order.
The main take away is that you need to express the cardinality in some way that preserves order and
choice
is one option.
Transactions.java
// This file was generated by the Eclipse Implementation of JAXB, v4.0.5
package generated;
import java.util.ArrayList;
import java.util.List;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElements;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"fileHeader",
"addendaOrBatchControlOrBatchHeader",
"fileControl"
})
@XmlRootElement(name = "Transactions")
public class Transactions {
@XmlElement(name = "FileHeader", required = true)
protected FileHeader fileHeader;
@XmlElements({
@XmlElement(name = "Addenda", type = Addenda.class),
@XmlElement(name = "BatchControl", type = BatchControl.class),
@XmlElement(name = "BatchHeader", type = BatchHeader.class),
@XmlElement(name = "Entry", type = Entry.class)
})
protected List<Object> addendaOrBatchControlOrBatchHeader;
@XmlElement(name = "FileControl", required = true)
protected FileControl fileControl;
public FileHeader getFileHeader() {
return fileHeader;
}
public void setFileHeader(FileHeader value) {
this.fileHeader = value;
}
public List<Object> getAddendaOrBatchControlOrBatchHeader() {
if (addendaOrBatchControlOrBatchHeader == null) {
addendaOrBatchControlOrBatchHeader = new ArrayList<>();
}
return this.addendaOrBatchControlOrBatchHeader;
}
public FileControl getFileControl() {
return fileControl;
}
public void setFileControl(FileControl value) {
this.fileControl = value;
}
}