我正在尝试使用StAX(我已经不喜欢它了......)
似乎使用它的唯一方法是通过连续的if-else条件
但最重要的是,似乎没有办法将元素与其子元素相关联,除非事先知道它
正在解析的xml文档的结构。这是正确的吗?
我尝试过以下方法:
我在字符串
<ns1:Root xmlns:ns1=\"http://rootNameSpace.com/\">
<ns1:A/>
<ns1:B>
<Book xmlns=\"http://www.myNameSpace.com\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
<Data>
<Author>John</Author>
<Edition>1</Edition>
<PubHouse>Small Publishing House</PubHouse>
<Price>37.8</Price>
</Data>
</Book>
</ns1:B>
</ns1:Root>
我想使用StAX来获取Book元素,但似乎我只能编写硬编码所有结构的代码。
即使用XMLEventReader和一次
你得到Book,开始循环数据,作者等。
对此有一个通用的解决方案吗?
我尝试了以下内容来解决这个问题:我试图从String转到XMLEventReader并返回String但我无法获得我最初使用的确切的String表示(名称空间在括号中,额外的冒号等)。
StringBuilder xml = new StringBuilder();
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
String msg = "<ns1:Root xmlns:ns1=\"http://rootNameSpace.com/\"><ns1:A/><ns1:B><Book xmlns=\"http://www.myNameSpace.com\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Data><Author>John</Author><Edition>1</Edition><PubHouse>Small Publishing House</PubHouse><Price>37.8</Price></Data></Book></ns1:B></ns1:Root>";
InputStream input = new ByteArrayInputStream(msg.getBytes("UTF-8"));
XMLEventReader xmlEventReader = inputFactory.createXMLEventReader(input);
while (xmlEventReader.hasNext())
{
XMLEvent event = xmlEventReader.nextEvent();
StringWriter sw = new StringWriter();
event.writeAsEncodedUnicode(sw);
xml.append(sw);
}
System.out.println(xml);
我得到以下内容:
<?xml version="1.0" encoding='UTF-8' standalone='no'?><['http://rootNameSpace.com/']:ns1:Root xmlns:ns1='http://rootNameSpace.com/'><['http://rootNameSpace.com/']:ns1:A></ns1:A><['http://rootNameSpace.com/']:ns1:B><['http://www.myNameSpace.com']::Book xmlns:='http://www.myNameSpace.com' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><['http://www.myNameSpace.com']::Data><['http://www.myNameSpace.com']::Author>John</Author><['http://www.myNameSpace.com']::Edition>1</Edition><['http://www.myNameSpace.com']::PubHouse>Small Publishing House</PubHouse><['http://www.myNameSpace.com']::Price>37.8</Price></Data></Book></ns1:B></ns1:Root>
这种情况可以通过StAX来解决,还是DOM是唯一的解决方案?
答案 0 :(得分:5)
我真的不明白你要做什么,但如果你想要标签的本地名称导致START_ELEMENT
事件,你可以这样做:
if (event.getEventType() == START_ELEMENT) {
QName qname = event.asStartElement().getName()
System.out.println("Start of element " + qname.getLocalPart());
}
同样,asEndElement
,asCharacters
等提供对其他类型节点的访问。
就个人而言,在大多数情况下,我通常会发现XMLStreamReader
对我来说更方便,但我认为这取决于用例以及您个人的偏好。专家提示,架构越严格,数据就越容易用StAX解析。
您可能还希望查看JAX-B自动XML数据绑定。
编辑:以下是OP中XML的天真递归下降StAX解析器:
@Test
public void recursiveDescentStaxParser( ) throws XMLStreamException,
FactoryConfigurationError
{
String msg = "<ns1:Root xmlns:ns1=\"http://rootNameSpace.com/\"><ns1:A/><ns1:B><Book xmlns=\"http://www.myNameSpace.com\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Data><Author>John</Author><Edition>1</Edition><PubHouse>Small Publishing House</PubHouse><Price>37.8</Price></Data></Book></ns1:B></ns1:Root>";
XMLStreamReader reader = XMLInputFactory.newFactory( )
.createXMLStreamReader( new StringReader( msg ) );
reader.nextTag( );
readRoot( reader );
}
private void readRoot( XMLStreamReader reader ) throws XMLStreamException
{
while ( reader.nextTag( ) == XMLEvent.START_ELEMENT )
{
QName name = reader.getName( );
if ( "B".equals( name.getLocalPart( ) ) )
readBooks( reader );
else
reader.nextTag( ); // Empty <A>
}
}
private void readBooks( XMLStreamReader reader ) throws XMLStreamException
{
while ( reader.nextTag( ) == XMLEvent.START_ELEMENT )
{
QName name = reader.getName( );
if ( !"Book".equals( name.getLocalPart( ) ) )
throw new XMLStreamException( name.toString( ) );
reader.nextTag( ); // Jump to <Data>
readBook( reader );
reader.nextTag( ); // Jump to </B>
}
}
private void readBook( XMLStreamReader reader ) throws XMLStreamException
{
reader.nextTag( ); // Skip to <Author>
System.out.println( "Author: " + reader.getElementText( ) );
reader.nextTag( ); // Skip to <Edition>
System.out.println( "Edition: " + reader.getElementText( ) );
reader.nextTag( ); // Skip to <PubHouse>
System.out.println( "Publisher: " + reader.getElementText( ) );
reader.nextTag( ); // Skip to <Price>
System.out.println( "Price: " + reader.getElementText( ) );
reader.nextTag( ); // Skip to </Book>
}
写这样的东西不仅使代码更易于阅读和推理,而且还会在错误弹出时跟踪堆栈。
答案 1 :(得分:1)
听起来你可能在这里选择了错误的工具:Stax是一个很好的API,用于高效处理大内容。但是,如果方便比效率更重要,是的,您可能应该考虑树模型(不一定是DOM,例如XOM)或数据绑定(JAXB或XStream)。具体来说,像SAX这样的Stax是基于流的,因此您只能看到当前事件或令牌。没有儿童或父母的访问者,因为没有保证可以访问它们,因为考虑到当前流的位置,这不一定是可能的。
但是如果要考虑性能或内存使用情况,您仍然可以考虑JAXB(通常比DOM模型更高效)或StaxMate。 StaxMate是Stax上的高性能,低内存使用扩展,使用起来更方便。 虽然您仍然需要按文档顺序迭代元素,但它的光标方法会更自然地映射父级子查找。所以它可能适合你的情况。