enzo
enzo

Reputation: 886

StAX - XMLEventReader unexpected behavior

I've just tried utilize StAX to parse some trivial XML document

<?xml version="1.0"?>
<root>
    <Employee>
        <name>John</name>
    </Employee>
    <Employee>
        <name>Lisa</name>
    </Employee>
</root>

...but faced a problem which makes me crazy. Here is the code:

public class Foo {

    public String name;

    @Override
    public String toString() {
        return "Foo{" + "name='" + name + '\'' + '}';
    }
}

public class StAXParserTest {

    @Test
    public void testFoo() throws Exception {
        List<Foo> result = new ArrayList<>();
        XMLEventReader eventReader = XMLInputFactory
                .newInstance()
                .createXMLEventReader(getResourceAsStream("example.xml"));
        while (eventReader.hasNext()) {
            XMLEvent event = eventReader.nextEvent();
            if (event.isStartElement()) {
                StartElement startElem = event.asStartElement();
                switch (startElem.getName().getLocalPart()) {
                    case "Employee":
                        result.add(parseFoo(eventReader));
                        break;
                }
            }
        }
        System.out.println(result);
    }

    private static Foo parseFoo(XMLEventReader eventReader) throws XMLStreamException {
        Foo foo = new Foo();
        while (true) {
            XMLEvent event = eventReader.nextEvent();
            if (event.isStartElement()) {
                switch (event.asStartElement().getName().getLocalPart()) {
                    case "name":
                        foo.name = eventReader.nextEvent().asCharacters().getData();
                        break;
                }
            }

            if (event.isEndElement() && event.asEndElement().getName().getLocalPart().equals("Foo")) {
                return foo;
            }
        }
    }

    private InputStream getResourceAsStream(String filename) throws URISyntaxException {
        return this.getClass().getClassLoader().getResourceAsStream(filename);
    }
}

There is nothing wrong it. But if you run the test you'll get the following error.

java.util.NoSuchElementException
    at com.sun.xml.internal.stream.XMLEventReaderImpl.nextEvent(XMLEventReaderImpl.java:88)
    at stax.StAXParserTest.parseFoo(StAXParserTest.java:40)
    at stax.StAXParserTest.testFoo(StAXParserTest.java:29)

I've spent an hour to figure out the cause. In order to solve the problem POJO class name must be EXACTLY the same as XML tag name (i.e. Foo.class should be renamed to Employee.class) and than it works:

[Employee{name='John'}, Employee{name='Lisa'}]

So my question is W-H-Y-YYYYYY? It's absolutely non-intuitive. This is not JAXB. Not any other object mapper. If I do all the work by myself than why class name matters?

P.S. Initially, it was Employee.class and <employee/> problem, but I simplified the test case to emphasize on class name.

Upvotes: 1

Views: 659

Answers (1)

minus
minus

Reputation: 2786

You must check employee as ending node, not "foo".

        if (event.isEndElement() && event.asEndElement()
            .getName().getLocalPart()
            .equals("Employee")) {
            return foo;
        }

Upvotes: 2

Related Questions