How to convert XML file to java objects / Converting XML file to java objects using JAXB

Practical Scenario

Suppose your application has to read an XML file and store its contents in a database such as each set of nodes is created as a record. For Example, there are multiple employee nodes with employee details in your xml and you have to store each employee details as a record in database. The process would be pretty easy if you have some mechanism to get the employee details form the XML in the form of java objects as then you could iterate those objects and store them by using JDBC or directly save them using any ORM framework (such as Hibernate).

How ?

The method discussed here uses JAXB (Java Architecture For XML Binding). JAXB is a java framework which has the ability to map classes to XML elements and vice-versa. It provides options to convert java objects to XML and XML to java objects provided they have been properly mapped to each other. The process of converting objects to XML is called marshalling (How to convert object to XML using JAXB) and the conversion of XML to objects is called unmarshalling in JAXB terms. So we will be discussing unmarshalling concept here.

Suppose you have the following XML structure which you want to convert to java objects :

<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”?>
<Employees>
<Employee>
<department>dev</department>
<name>codippa</name>
</Employee>
<Employee>
<department>qa</department>
<name>emp1</name>
</Employee>
<Employee>
<department>service</department>
<name>emp2</name>
</Employee>
<Employee>
<department>support</department>
<name>emp3</name>
</Employee>
</Employees>

If you visualize this XML structure and co-relate this with java objects then you can see that this XML is a collection of employees and each employee has two attributes (or fields), department and name. Thus, you can map this XML to two classes

  1. Employees.java – This will be the root of XML and should contain a list of employees.
  2. Employee.java – This class should contain two fields namely department and name.

Let’s design our java classes based on above concept.

Employees.java

 import java.util.List;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;

 @XmlRootElement(name="Employees")
 public class Employees {
   private List employee;

   @XmlElement(name="Employee")
   public List getEmployee() {
       return employee;
   }

   public void setEmployee(List employeeList) {
       this.employee = employeeList;
   }
 }

Details

Employees.java class is annotated with @XmlRootElementwhich implies that it will be the root of the generated XML document. nameattribute of this element specifies the name with which the root node of XML will be created and is optional. If it is not given then root will be created by the name of the class in lower-case letters (that is, employees).

The class also contains a list of Employeeobjects with its getter method annotated with @XmlElementwhich specifies that the elements of this class will be the nodes of the XML with each node named as Employee. Again, the name attribute of this annotation is optional and specifies the name with which the child nodes will be created. If not given, the child nodes will be created by the name of Employeeclass in lower case.

Employee.java

import javax.xml.bind.annotation.XmlElement;

public class Employee {
	
	private String department;

	private String name;
	@XmlElement
	public String getDepartment() {
		return department;
	}

	public void setDepartment(String department) {
		this.department = department;
	}

	@XmlElement
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Details

The above class contains two fields departmentand namewhose getter methods are annotated with @XmlElement.This annotation signifies that these fields will become nodes in the generated XML and will be the children of the class node which contains them. By default the nodes will be created by the name of fields. If you want some other name for the nodes, then provide nameattribute of this annotation with the desired value as shown in Employees class.

Moving Ahead

We also need to write code which shall read the XML file and converts them to objects by unmarshalling. Following code reads the XML and converts them to a list of Employees:

     import java.io.FileNotFoundException;
     import java.io.FileReader;
     import java.io.IOException;
     import java.io.StringReader;
     import java.io.StringWriter;
     import java.util.List;
     
     import javax.xml.bind.JAXBContext;
     import javax.xml.bind.JAXBException;
     import javax.xml.bind.Marshaller;
     import javax.xml.bind.Unmarshaller;
     public static void main(String[] args) throws IOException, JAXBException {
        //get file content in the form of string
	String xml = getFileContent("jaxbTest.xml");
        //initialize a java.io.Reader object with xml content
        StringReader reader = new StringReader(xml);
        //initialize jaxb classes
	JAXBContext context = JAXBContext.newInstance(Employees.class);
	Unmarshaller un = context.createUnmarshaller();
	//convert to desired object
	Employees employeeData = (Employees)un.unmarshal(reader);
	List employees = employeeData.getEmployee();
        //iterate over object
	for(Employee e: employees){
		System.out.println("Name : "+e.getName());
		System.out.println("Department : "+e.getDepartment());
		System.out.println("--------------------------");
    }

  /**
     * Reads file content
     * @param filePath
     * @return
    */
    static String getFileContent(String filePath) {
	FileReader fr = null;
	char[] buffer = new char[1024];
	StringBuffer fileContent = new StringBuffer();
	try {
		fr = new FileReader(filePath);
		int i = 0;
		while ((i = fr.read(buffer)) != -1) {
			fileContent.append(new String(buffer));
		}
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		if (fr != null) {
			try {
			    fr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
		return fileContent.toString();
	}

Output

Name : codippa
Department : dev
————————–
Name : emp1
Department : qa
————————–
Name : emp2
Department : service
————————–
Name : emp3
Department : support
————————–

Details

In order to convert XML to an object, first create an object of javax.xml.bind.JAXBContext class using its static newInstancemethod. This method takes a list of one or more top-level classes. Top-level means the class which should be mapped to the root of the XML. In our case, it is Employees class. Object of javax.xml.bind.JAXBContextis then used to create an object of javax.xml.bind.Unmarshallar which is responsible for converting XML content to java object graph. Itsunmarshalmethod takes a java.io.Reader object which contains the content of XML file. Thus object of any class which extends java.io.Readermay be passed to this method.
In above example we pass an object of java.io.StringReader.This StringReader is initialized with the contents of XML read from the XML file in the form of String. Learn How to convert the contents of a file to String in various ways
The result of unmarshalling is an object of Employees class with its employeefield populated with all the employees listed in the XML which can further be used as you want.

Let’s tweak in

  1. @XmlElement annotation may also be applied over field names rather than their getter methods. In that case an annotation @XmlAccessorType(XmlAccessType.FIELD) needs to be applied at the class level.
  2. If your XML nodes also have attributes such as of the form <Employee id="1">then this may be achieved by adding an id field to Employee class and annotating it with @XmlAttributeannotation. Id field each Employee object will automatically be populated with the value in XML.
  3. Even if a field in your JAXB annotated classes is not annotated with @XmlElementannotation, it will be populated by default.
  4. If a field is marked with @XmlTransientannotation, then it will not be populated and will have a default value according to its data type (nullfor String,falsefor boolean etc.).
  5. All JAXB code used above throws javax.xml.bind.JAXBException. Either handle it using try-catch blocks or throw it.

Share your thoughts on how you liked this post. coDippa !!!

Leave a Reply