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
- Employees.java – This will be the root of XML and should contain a list of employees.
- Employee.java – This class should contain two fields namely
department
andname
.
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 @XmlRootElement
which implies that it will be the root of the generated XML document. name
attribute 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 Employee
objects with its getter method annotated with @XmlElement
which 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 Employee
class 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 department
and name
whose 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 name
attribute 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 newInstance
method. 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.JAXBContext
is then used to create an object of javax.xml.bind.Unmarshallar
which is responsible for converting XML content to java object graph. Itsunmarshal
method takes a java.io.Reader
object which contains the content of XML file. Thus object of any class which extends java.io.Reader
may 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 employee
field populated with all the employees listed in the XML which can further be used as you want.
Let’s tweak in
@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.- If your XML nodes also have attributes such as of the form
<Employee id="1">
then this may be achieved by adding anid
field to Employee class and annotating it with@XmlAttribute
annotation. Id field each Employee object will automatically be populated with the value in XML. - Even if a field in your JAXB annotated classes is not annotated with
@XmlElement
annotation, it will be populated by default. - If a field is marked with
@XmlTransient
annotation, then it will not be populated and will have a default value according to its data type (null
for String,false
for boolean etc.). - All JAXB code used above throws
javax.xml.bind.JAXBException
. Either handle it usingtry-catch
blocks or throw it.
Share your thoughts on how you liked this post. coDippa !!!