【问题标题】:java use StAX to get children elements in a generic fashionjava使用StAX以通用方式获取子元素
【发布时间】:2011-05-14 23:02:03
【问题描述】:

我正在尝试使用 StAX(我已经不喜欢它了....)
似乎使用它的唯一方法是通过连续的 if-else 条件。
但最重要的是似乎没有办法将一个元素与其子元素关联起来,除非事先知道 正在解析的 xml 文档的结构。这样正确吗?
我尝试了以下方法: 我在字符串中有这个 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 元素,但似乎我只能编写对所有结构进行硬编码的代码。
IE。使用 XMLEventReader 和一次 你得到书,开始循环数据,作者等。
有没有通用的解决方案?
我尝试了以下方法来解决这个问题:我尝试从 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 是唯一的解决方案?

【问题讨论】:

    标签: java dom stax jaxp


    【解决方案1】:

    我不太明白你想做什么,但如果你想要标签的本地名称导致 START_ELEMENT 事件,你可以这样做:

    if (event.getEventType() == START_ELEMENT) {
        QName qname = event.asStartElement().getName()
        System.out.println("Start of element " + qname.getLocalPart());
    }
    

    同样,asEndElementasCharacters 等提供对其他类型节点的访问。

    就个人而言,我通常发现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>
    
    }
    

    编写这样的东西不仅使代码更易于阅读和推理,而且还可以在弹出错误时跟踪堆栈。

    【讨论】:

    • @gustafc:通过你发布的代码,我知道一个元素开始了。我怎样才能得到这个元素的所有子元素?使用 DOM 很简单。你如何使用 StAX?
    • 好吧,要获取元素“内部”的所有事件,您只需读取事件,直到找到比START_ELEMENT 事件多一个END_ELEMENT。但这是一种可怕的 DOMish 方式,您真正想要做的是递归下降解析器,它读取每个元素并将它们即时转换为某个域对象。对不起,糟糕的解释,但学习有效地使用 StAX 实际上主要是为了摆脱你的 DOM 成瘾。
    • @gustafc:我是这么想的。说“递归下降解析器”是指不使用 StAX 吗?所以 StAX 不会在不使用“黑客”的情况下为您提供这个?
    • @@gustafc: 非常感谢您的回答!不过我最担心的是这段代码是特定于我发布的 xml 文档示例的。我的意思是,必须事先知道 xml 文档结构正在解析。是否可以修改此代码以便对任何 xml 文档通用(即事先不知道结构)?
    • 嗯,使用 StAX,您可以轻松实现 SAX 或 DOM 解析器(相对而言 :),因此您可以使用 SAX/DOM 做的任何事情都可以使用 StAX。当然,它并不总是最好的选择,但我发现我通常更喜欢 StAX 而不是其他 API。您编写的解析器确实倾向于变得相当特定于您正在阅读的 XML,但这往往不是一件好事(除非您有一个非常混乱的架构要解释)。
    【解决方案2】:

    听起来您可能在这里选择了错误的工具:Stax 是一个很棒的 AP​​I,可用于有效处理大型内容。但是如果方便比效率更重要,是的,您可能应该考虑树模型(不一定是 DOM,例如 XOM 更好)或数据绑定(JAXB 或 XStream)。具体来说,像 SAX 这样的 Stax 是基于流的,因此您只能看到当前事件或令牌。没有儿童或父母的访问器,因为没有保证可以访问他们的方法,因为考虑到当前的流位置,这不一定是可能的。

    但是如果性能或内存使用是一个问题,您仍然可以考虑 JAXB(它通常比 DOM 等树模型更有效)或StaxMate。 StaxMate 是对 Stax 的高性能、低内存使用扩展,使用起来更方便。 虽然您仍然需要按文档顺序迭代元素,但它的光标方法更自然地映射到父子查找。所以它可能适用于你的情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-02
      • 1970-01-01
      • 2012-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      相关资源
      最近更新 更多