javaxmlxml-parsingxsdsaxparser

extract attributes from SAX current element?


I have to parse xml file with xsd validation. And I strike one point and can't find solution.

here is how xml file loooks:

<?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>

xsd validation:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsd:element name="staff">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="employee" maxOccurs="unbounded" minOccurs="0"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="employee">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element type="xsd:string" name="name"/>
                <xsd:element type="xsd:int" name="salary"/>
                <xsd:element name="hiredate">
                    <xsd:complexType>
                        <xsd:simpleContent>
                            <xsd:extension base="xsd:string">
                                <xsd:attribute type="xsd:short" name="year" use="optional"/>
                                <xsd:attribute type="xsd:byte" name="month" use="optional"/>
                                <xsd:attribute type="xsd:byte" name="day" use="optional"/>
                            </xsd:extension>
                        </xsd:simpleContent>
                    </xsd:complexType>
                </xsd:element>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

I checked it with xml validator. All is correct.

Here is my snippet of code:

public class SaxParserDemo {
    public static void main(String[] args) {
        Schema schema;
        String language = XMLConstants.W3C_XML_SCHEMA_NS_URI;
        SchemaFactory schemaFactory = SchemaFactory.newInstance(language);

        try {
            schema = schemaFactory.newSchema(new File(EMPLOYEE_XSD.getFilename())); // create new xml schema
            SAXParserFactory factory = SAXParserFactory.newInstance();
            factory.setSchema(schema);  // set schema to the factory

            InputStream xmlInput = new FileInputStream(EMPLOYEE_XML.getFilename());
            SAXParser saxParser = factory.newSAXParser();
            SaxHandler handler = new SaxHandler();
            saxParser.parse(xmlInput, handler);

            for (Employee employee : handler.employees) {
                System.out.println(employee);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

class SaxHandler extends DefaultHandler {

    private Stack<String> elementStack = new Stack<>();
    private Stack<Object> objectStack = new Stack<>();

    public List<Employee> employees = new ArrayList<>();

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        this.elementStack.push(qName);

        if ("employee".equals(qName)) {
            Employee employee = new Employee();
            this.objectStack.push(employee);
            this.employees.add(employee);
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        this.elementStack.pop();

        if ("employee".equals(qName)) {
            Object objects = this.objectStack.pop();
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        String value = new String(ch, start, length).trim();
        if (value.length() == 0) return;        // skip white space

        if ("name".equals(currentElement())) {
            Employee employee = (Employee) this.objectStack.peek();
            employee.setName(value);
        } else if ("salary".equals(currentElement()) && "employee".equals(currentParrentElement())) {
            Employee employee = new Employee();
            employee.setSalary(Double.parseDouble(value));
        } else if ("hiredate".equals(currentElement()) && "employee".equals(currentParrentElement())) {
            Employee employee = new Employee();
            // I stuck here 
//            employee.setHireDay();
        }
    }

    private String currentElement() {
        return this.elementStack.peek();
    }

    private String currentParrentElement() {
        if (this.elementStack.size() < 2) return null;
        return this.elementStack.get(this.elementStack.size() - 2);
    }

}

I have to extract data from element attributes. I couldn't figure out how to implement it. I'm using SAX first time. Any suggestions?

Employees class has two setters for hireday:

setHireDay(Date hireDay)

setHireDay(int year, int month, int day)

How to solve this issue?


Solution

  • Like this:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Stack;
    
    import javax.xml.XMLConstants;
    import javax.xml.parsers.SAXParser;
    import javax.xml.parsers.SAXParserFactory;
    import javax.xml.validation.Schema;
    import javax.xml.validation.SchemaFactory;
    
    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    
    public class SaxParserDemo {
        public static void main(String[] args) {
            Schema schema;
            String language = XMLConstants.W3C_XML_SCHEMA_NS_URI;
            SchemaFactory schemaFactory = SchemaFactory.newInstance(language);
    
            try {
                schema = schemaFactory.newSchema(new File("src/newEmployee.xsd")); // create new xml schema
                SAXParserFactory factory = SAXParserFactory.newInstance();
                factory.setSchema(schema);  // set schema to the factory
    
                InputStream xmlInput = new FileInputStream("src/empl.xml");
                SAXParser saxParser = factory.newSAXParser();
                SaxHandler handler = new SaxHandler();
                saxParser.parse(xmlInput, handler);
    
                for (Employee employee : handler.employees) {
                    System.out.println(employee.getName());
                    System.out.println(employee.getSalary());
                    System.out.println(employee.getHireDate());
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }
    
    class SaxHandler extends DefaultHandler {
    
        private Stack<String> elementStack = new Stack<String>();
        private Stack<Object> objectStack = new Stack<Object>();
    
        public List<Employee> employees = new ArrayList<Employee>();
        Employee employee=null;
    
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            this.elementStack.push(qName);
            
            if ("employee".equals(qName)) {
                
                if (employee!=null) {
                      this.employees.add(employee);
                }
                employee = new Employee();
                this.objectStack.push(employee);
            }
    
            if ("hiredate".equals(qName)) {
                 String yearatt=attributes.getValue("year");
                 System.out.println(yearatt);
                 String monthatt=attributes.getValue("month");
                 System.out.println(monthatt);
                 String dayatt=attributes.getValue("day");
                 System.out.println(dayatt);
                 employee.setHireDay( yearatt,  monthatt,  dayatt) ;
            }
        }
    
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            this.elementStack.pop();
    
            if ("employee".equals(qName)) {
                Object objects = this.objectStack.pop();
            }
        }
    
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            String value = new String(ch, start, length).trim();
            if (value.length() == 0) return;        // skip white space
    
            if ("name".equals(currentElement())) {
                 employee = (Employee) this.objectStack.peek();
                employee.setName(value);
            } else if ("salary".equals(currentElement()) && "employee".equals(currentParrentElement())) {
                
                employee.setSalary(Double.parseDouble(value));
            } else if ("hiredate".equals(currentElement()) && "employee".equals(currentParrentElement())) {
            
                // I stuck here 
    //            employee.setHireDay();
            }
        }
    
        private String currentElement() {
            return this.elementStack.peek();
        }
    
        private String currentParrentElement() {
            if (this.elementStack.size() < 2) return null;
            return this.elementStack.get(this.elementStack.size() - 2);
        }
     }