I have to parse xml file with stax and I did mostly what I need except one point.
My code logic doesn't extract attributes at right. My output looks next:
Employee { name=Carl Cracker, salary=75000.0, hireDay=null }
Employee { name=Harry Hacker, salary=50000.0, hireDay=null }
Employee { name=Tony Tester, salary=40000.0, hireDay=null }
Here is content of xml file:
<?xml version="1.0" encoding="UTF-8"?>
<staff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="newEmployee.xsd">
<employee>
<name>Carl Cracker</name>
<salary>75000</salary>
<hiredate year="1987" month="12" day="15" />
</employee>
<employee>
<name>Harry Hacker</name>
<salary>50000</salary>
<hiredate year="1989" month="10" day="1" />
</employee>
<employee>
<name>Tony Tester</name>
<salary>40000</salary>
<hiredate year="1990" month="3" day="15" />
</employee>
</staff>
Here is my code:
class StaxXmlParser {
private List<Employee> employeeList;
private Employee currentEmployee;
private String tagContent;
private XMLStreamReader reader;
public StaxXmlParser(String filename) {
employeeList = null;
currentEmployee = null;
tagContent = null;
try {
XMLInputFactory factory = XMLInputFactory.newFactory();
reader = factory.createXMLStreamReader(new FileInputStream(new File(filename)));
parseEmployee();
} catch (Exception e) {
e.printStackTrace();
}
}
public List<Employee> parseEmployee() throws XMLStreamException {
while (reader.hasNext()) {
int event = reader.next();
switch (event) {
case XMLStreamConstants.START_ELEMENT:
if ("employee".equals(reader.getLocalName())) {
currentEmployee = new Employee();
}
if ("staff".equals(reader.getLocalName())) {
employeeList = new ArrayList<>();
}
if ("hireday".equals(reader.getLocalName())) {
int yearAttr = Integer.parseInt(reader.getAttributeValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "year"));
int monthAttr = Integer.parseInt(reader.getAttributeValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "month"));
int dayAttr = Integer.parseInt(reader.getAttributeValue(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "day"));
currentEmployee.setHireDay(yearAttr, monthAttr, dayAttr);
}
break;
case XMLStreamConstants.CHARACTERS:
tagContent = reader.getText().trim();
break;
case XMLStreamConstants.END_ELEMENT:
switch (reader.getLocalName()) {
case "employee":
employeeList.add(currentEmployee);
break;
case "name":
currentEmployee.setName(tagContent);
break;
case "salary":
currentEmployee.setSalary(Double.parseDouble(tagContent));
break;
}
}
}
return employeeList;
}
}
here is content of Employee
class:
class Employee {
private String name;
private double salary;
private Date hireDay;
public Employee() {}
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public Date getHireDay() {
return hireDay;
}
public void setName(String name) {
this.name = name;
}
public void setSalary(double salary) {
this.salary = salary;
}
public void setHireDay(Date hireDay) {
this.hireDay = hireDay;
}
public void setHireDay(int year, int month, int day) {
GregorianCalendar calendar = new GregorianCalendar(year, month, day);
this.hireDay = calendar.getTime();
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
@Override
public String toString() {
return String.format("Employee { name=%s, salary=%s, hireDay=%s }", name, salary, hireDay);
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
How to extract attributes by stax?
I see 3 issues with your code here:
You are retrieving hireday
instead of hiredate
tag from XML; should be: if ("hiredate".equals(reader.getLocalName()))
You are retrieving the individual attributes with a namespace where your XML does not have any. Change it to NULL
, like:
int yearAttr = Integer.parseInt(reader.getAttributeValue(null, "year"));
Calendar class in Java considers months starting from 0 (0 being January). Hence your value 12 would move on to the next year. You may want to subtract 1 from the month value to get the desired output.
currentEmployee.setHireDay(yearAttr, monthAttr - 1, dayAttr);
Your modified parseEmployee()
:
public List<Employee> parseEmployee() throws XMLStreamException {
while (reader.hasNext()) {
int event = reader.next();
switch (event) {
case XMLStreamConstants.START_ELEMENT:
if ("employee".equals(reader.getLocalName())) {
currentEmployee = new Employee();
}
if ("staff".equals(reader.getLocalName())) {
employeeList = new ArrayList<>();
}
if ("hiredate".equals(reader.getLocalName())) {
int yearAttr = Integer.parseInt(reader.getAttributeValue(
null, "year"));
int monthAttr = Integer.parseInt(reader.getAttributeValue(
null, "month"));
int dayAttr = Integer.parseInt(reader.getAttributeValue(
null, "day"));
currentEmployee
.setHireDay(yearAttr, monthAttr - 1, dayAttr);
}
break;
case XMLStreamConstants.CHARACTERS:
tagContent = reader.getText().trim();
break;
case XMLStreamConstants.END_ELEMENT:
switch (reader.getLocalName()) {
case "employee":
employeeList.add(currentEmployee);
break;
case "name":
currentEmployee.setName(tagContent);
break;
case "salary":
currentEmployee.setSalary(Double.parseDouble(tagContent));
break;
}
}
}
return employeeList;
}
Now you get the correct output:
Employee { name=Carl Cracker, salary=75000.0, hireDay=Tue Dec 15 00:00:00 IST 1987 }
Employee { name=Harry Hacker, salary=50000.0, hireDay=Sun Oct 01 00:00:00 IST 1989 }
Employee { name=Tony Tester, salary=40000.0, hireDay=Thu Mar 15 00:00:00 IST 1990 }