Although JAXB requires a top level XML element, it does not require the immediate, physical container to be a file. The message can be unmarshaled as an input stream from anywhere. We can use this observation to contain each XML message in its own Base64
stream which can then be stored as a list of streams (line-by-line) in one meta-container file.
This example reads each line of an input file as a Base64
encoded stream. Each encoded line contains one Employee
XML element; thus, each line can be unmarshaled and put into a list of Employee
objects. Also, the object list can be marshaled, encoded and stored as a single file.
Execution
This is a stand-alone Maven project (zip). You can run the test using:
mvn -Ptest clean test
The output shows the test results.
This Maven project includes:
- An XML Schema file employee.xsd for the
Employee
model
- A JAXB binding file employee.xjb for customizations
- A JUnit test class EmployeeTest to demonstrate (un)marshalling
- Sample XML files with
Employee
data.
- The Maven POM file with
hisrc-higherjaxb-maven-plugin
Employees
src
main
java
resources
employee.xjb
employee.xsd
test
java
org/example/employee/EmployeeTest.java
resources
simplelogger.properties
samples
Employee1.xml
Employee2.xml
Employees.b64
pom.xml
The JAXB classes are generated by this plugin in this project's pom.xml
<plugin>
<groupId>org.patrodyne.jvnet</groupId>
<artifactId>hisrc-higherjaxb-maven-plugin</artifactId>
<version>${hisrc-higherjaxb-maven-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
and are generated to:
target/generated-sources/xjc/
org.example.employee
Employee.java
ObjectFactory.java
Testing
The JUnit test class, EmployeeTest, scans for the sample files and invokes the method checkSample(File sample)
to provide each file to the tester. For this project, a JAXBContext
is created and each file in the samples path is unmarshaled to an employee
object. When successful, the employee
object is marshaled for logging and your review.
In particular, the data file Employees.b64 demonstrates how a list of XML streams can be stored in one file. The sample file contains two Base64 encoded streams, each on its own line; but, there can be any number of such lines, each representing an XML stream that JAXB can unmarshal.
This code fragment from EmployeeTest
shows how a data source file can be read line-by-line to decode and unmarshal the Employee
data. Then, in reverse, each Employee
object is marshaled and encoded before writing the list to a target file.
EmployeeTest#testEmployees()
...
// Read Source
try ( FileReader fr = new FileReader(employeesB64Source) )
{
LineNumberReader lnr = new LineNumberReader(fr);
String employeeB64 = null;
while ( (employeeB64 = lnr.readLine()) != null )
{
String employeeXml = new String(decoder.decode(employeeB64), UTF_8);
Object root = unmarshaller.unmarshal(new StringReader(employeeXml));
if ( root instanceof Employee )
employeeList.add((Employee) root);
}
}
...
...
// Write Target
try ( FileWriter fw = new FileWriter(employeesB64Target) )
{
for ( Employee employee : employeeList )
{
String employeeXml = null;
employeeXml = marshalToString(employee, marshaller);
String employeeB64 = encoder.encodeToString(employeeXml.getBytes(UTF_8));
fw.write(employeeB64 + nl);
}
}
Bash Bonus
As a bonus, the Linux Bash base64
command can be used to quickly decode all lines in the b64 file:
Bash Shell
$ base64 --decode Employees.b64
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<id>1</id>
<firstName>Lokesh</firstName>
<lastName>Gupta</lastName>
<income>100.0</income>
</employee>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<id>2</id>
<firstName>John</firstName>
<lastName>Mclane</lastName>
<income>200.0</income>
</employee>