Code Breaker
Code Breaker

Reputation: 1

How would I marshal/ unmarshal a List of java objects to an XML without a root element?

I have a List of java objects with structure as follows

@XmlRootElement(name = "employee")
@XmlAccessorType (XmlAccessType.FIELD)
public class Employee 
{
  private Integer id;
  private String firstName;
  private String lastName;
  private double income;
   
  //Getters and Setters
}

I want to convert it to an XML file whose contents would just be the list without any root element.

Expected Output:

< ?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<employee>
    <id>l</id>
    <firstName>Lokesh</firstName>
    <lastName>Gupta</lastName>
    <income>100. 0</income>
</employee>
<employee>
    <id>2</id>
    <firstName>John</firstName>
    <lastName>Mclane</lastName>
    <income>200. 0</income>
</employee>

Is there a way to achieve this with Jaxb classes or any other java to xml libraries ? Also is there a way to unmarshall the same back to java POJO?

I tried to marshall a list as is but it just gave out an

If I tried to create a wrapper class and then marshall it, it would just add the wrapper

Code:

@XmlRootElement(name = "employees")
@XmlAccessorType (XmlAccessType.FIELD)
public class Employees 
{
  @XmlElement(name = "employee")
  private List<Employee> employees = null;
}

Output:

< ?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<employees>
    <employee>
        <id>l </id>
        <firstName>Lokesh</firstName>
        <lastName>Gupta</lastName>
        <income>100. 0</income>
    </employee>
    <employee>
        <id>2</id>
        <firstName>John</firstName>
        <lastName>Mclane</lastName>
        <income>200. 0</income>
    </employee>
</employees>

Upvotes: 0

Views: 124

Answers (1)

Rick O&#39;Sullivan
Rick O&#39;Sullivan

Reputation: 476

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>

Upvotes: 0

Related Questions