StAX - XMLEventReader 意外行为
StAX - XMLEventReader unexpected behavior
我刚刚尝试使用 StAX 来解析一些琐碎的 XML 文档
<?xml version="1.0"?>
<root>
<Employee>
<name>John</name>
</Employee>
<Employee>
<name>Lisa</name>
</Employee>
</root>
...但遇到了一个让我抓狂的问题。这是代码:
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);
}
}
没有错吧。但是如果你 运行 测试你会得到以下错误。
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)
我花了一个小时来找出原因。为了解决这个问题,POJO class 名称必须与 XML 标签名称完全相同(即 Foo.class 应该重命名为 Employee.class)并且比它有效:
[Employee{name='John'}, Employee{name='Lisa'}]
所以我的问题是 W-H-Y-YYYYYY?这绝对是不直观的。这不是 JAXB。不是任何其他对象映射器。如果我自己完成所有工作,为什么 class 名字很重要?
P.S。最初,这是 Employee.class
和 <employee/>
的问题,但我简化了测试用例以强调 class 名称。
你必须检查员工作为结束节点,而不是"foo"
。
if (event.isEndElement() && event.asEndElement()
.getName().getLocalPart()
.equals("Employee")) {
return foo;
}
我刚刚尝试使用 StAX 来解析一些琐碎的 XML 文档
<?xml version="1.0"?>
<root>
<Employee>
<name>John</name>
</Employee>
<Employee>
<name>Lisa</name>
</Employee>
</root>
...但遇到了一个让我抓狂的问题。这是代码:
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);
}
}
没有错吧。但是如果你 运行 测试你会得到以下错误。
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)
我花了一个小时来找出原因。为了解决这个问题,POJO class 名称必须与 XML 标签名称完全相同(即 Foo.class 应该重命名为 Employee.class)并且比它有效:
[Employee{name='John'}, Employee{name='Lisa'}]
所以我的问题是 W-H-Y-YYYYYY?这绝对是不直观的。这不是 JAXB。不是任何其他对象映射器。如果我自己完成所有工作,为什么 class 名字很重要?
P.S。最初,这是 Employee.class
和 <employee/>
的问题,但我简化了测试用例以强调 class 名称。
你必须检查员工作为结束节点,而不是"foo"
。
if (event.isEndElement() && event.asEndElement()
.getName().getLocalPart()
.equals("Employee")) {
return foo;
}