Question

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?

Was it helpful?

Solution

I see 3 issues with your code here:

  1. You are retrieving hireday instead of hiredate tag from XML; should be: if ("hiredate".equals(reader.getLocalName()))

  2. 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"));

  3. 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 }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top