Rin*_*nke 79 java xml stax sax xml-parsing
流式xml解析器(如SAX和StAX)比构建像DOM解析器这样的树结构的解析器更快,内存效率更高.SAX是一个推送解析器,意味着它是观察者模式的一个实例(也称为监听器模式).SAX首先存在,但随后出现了StAX - 一个拉解析器,意味着它基本上像迭代器一样工作.
你可以找到为什么在任何地方都喜欢StAX而不是SAX的原因,但它通常归结为:"它更容易使用".
在关于JAXP的Java教程中,StAX被模糊地呈现为DOM和SAX之间的中间:"它比SAX更容易,并且比DOM更高效".但是,我从来没有发现任何线索,StAX比SAX更慢或内存效率更低.
这一切让我想知道:有没有理由选择SAX而不是StAX?
Tho*_*sRS 80
概述
 XML文档是分层文档,其中相同的元素名称和命名空间可能出现在多个地方,具有不同的含义,并且具有不定深度(递归).正常情况下,解决大问题的方法是将它们分成小问题.在XML解析的上下文中,这意味着在特定于该XML的方法中解析XML的特定部分.例如,一个逻辑将解析一个地址:
<Address>
    <Street>Odins vei</Street>    
    <Building>4</Building>
    <Door>b</Door>
</Address>
即你会有一个方法
AddressType parseAddress(...); // A
要么
void parseAddress(...); // B
在你的逻辑中的某个地方,获取XML输入参数并返回一个对象(B的结果可以稍后从一个字段中获取).
SAX 
SAX'推送'XML 事件,由您决定XML事件在您的程序/数据中的位置.
// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
    // .. your logic here for start element
}
如果是"Building"开始元素,则需要确定您实际正在解析一个Address,然后将XML事件路由到其作业解释Address的方法.
StAX 
StAX'提取'XML 事件,由您决定在程序/数据中接收XML事件的位置.
// method in standard StAX reader
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
    // .. your logic here for start element
}
当然,您总是希望在方法中接收"Building"事件,该方法的作用是解释Address.
讨论
 
SAX和StAX之间的区别在于推拉.在这两种情况下,必须以某种方式处理解析状态.
这转换为SAX的典型方法B和StAX的方法A. 此外,SAX必须为B提供单独的XML事件,而StAX可以为A提供多个事件(通过传递XMLStreamReader实例).
因此,B首先检查解析的先前状态,然后处理每个单独的XML事件,然后存储状态(在字段中).方法A可以通过多次访问XMLStreamReader来一次处理XML事件,直到满意为止.
结束语
StAX允许您根据XML结构构建解析(数据绑定)代码 ; 因此,就SAX而言,"状态"是StAX的程序流隐含的,而在SAX中,对于大多数事件调用,您始终需要保留某种状态变量+根据该状态路由流.
除了最简单的文档,我建议使用StAX.而是稍后转向SAX作为优化(但是你可能希望到那时为二进制).
使用StAX解析时遵循此模式:
public MyDataBindingObject parse(..) { // provide input stream, reader, etc
        // set up parser
        // read the root tag to get to level 1
        XMLStreamReader reader = ....;
        do {
            int event = reader.next();
            if(event == XMLStreamConstants.START_ELEMENT) {
              // check if correct root tag
              break;
            }
            // add check for document end if you want to
        } while(reader.hasNext());
        MyDataBindingObject object = new MyDataBindingObject();
        // read root attributes if any
        int level = 1; // we are at level 1, since we have read the document header
        do {
            int event = reader.next();
            if(event == XMLStreamConstants.START_ELEMENT) {
                level++;
                // do stateful stuff here
                // for child logic:
                if(reader.getLocalName().equals("Whatever1")) {
                    WhateverObject child = parseSubTreeForWhatever(reader);
                    level --; // read from level 1 to 0 in submethod.
                    // do something with the result of subtree
                    object.setWhatever(child);
                }
                // alternatively, faster
                if(level == 2) {
                    parseSubTreeForWhateverAtRelativeLevel2(reader);
                    level --; // read from level 1 to 0 in submethod.
                    // do something with the result of subtree
                    object.setWhatever(child);
                }
            } else if(event == XMLStreamConstants.END_ELEMENT) {
                level--;
                // do stateful stuff here, too
            }
        } while(level > 0);
        return object;
}
所以子方法使用大致相同的方法,即计数水平:
private MySubTreeObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
    MySubTreeObject object = new MySubTreeObject();
    // read element attributes if any
    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;
            // do stateful stuff here
            // for child logic:
            if(reader.getLocalName().equals("Whatever2")) {
                MyWhateverObject child = parseMySubelementTree(reader);
                level --; // read from level 1 to 0 in submethod.
                // use subtree object somehow
                object.setWhatever(child);
            }
            // alternatively, faster, but less strict
            if(level == 2) {
              MyWhateverObject child = parseMySubelementTree(reader);
                level --; // read from level 1 to 0 in submethod.
                // use subtree object somehow
                object.setWhatever(child);
            }
        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }
    } while(level > 0);
    return object;
}
然后最终达到您将读取基本类型的级别.
private MySetterGetterObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {
    MySetterGetterObject myObject = new MySetterGetterObject();
    // read element attributes if any
    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;
            // assume <FirstName>Thomas</FirstName>:
            if(reader.getLocalName().equals("FirstName")) {
               // read tag contents
               String text = reader.getElementText()
               if(text.length() > 0) {
                    myObject.setName(text)
               }
               level--;
            } else if(reader.getLocalName().equals("LastName")) {
               // etc ..
            } 
        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }
    } while(level > 0);
    // verify that all required fields in myObject are present
    return myObject;
}
这很简单,没有误解的余地.只记得正确递减水平:
A.在您预期的字符之后但在某些标记中得到END_ELEMENT应该包含字符(在上面的模式中):
<Name>Thomas</Name>
相反
<Name></Name>
对于缺少的子树也是如此,你明白了.
B.调用在起始元素上调用的子分析方法,并在相应的结束元素之后返回,即解析器比方法调用之前低一级(上述模式).
注意这种方法如何完全忽略"可忽略的"空白,以实现更强大的实现.
解析器
 
使用Woodstox获取大多数功能,或使用Aaalto-xml获得速度.
Joh*_*erg 21
为了概括一点,我认为StAX可以有效率SAX.随着改进的设计StAX 我无法找到任何SAX解析首选的情况,除非使用遗留代码.
编辑:根据这个博客Java SAX vs. StAX StAX提供没有架构验证.
ag1*_*112 16
@Rinke:我想只有当你不需要处理/处理XML内容时,我才会考虑更喜欢SAX而不是STAX; 例如,你想要做的只是检查传入XML的格式是否正确并且只想处理错误...在这种情况下你可以简单地在SAX解析器上调用parse()方法并指定错误处理程序来处理任何错误解析问题....所以基本上STAX绝对是你想要处理内容的场景中的首选,因为SAX内容处理程序太难编码......
这种情况的一个实际示例可能是,如果您的企业系统中有一系列SOAP节点,并且入口级SOAP节点只允许那些SOAP XML通过下一阶段,这些都是格式良好的,那么我看不出任何理由为什么我会使用STAX.我只想使用SAX.
| 归档时间: | 
 | 
| 查看次数: | 42428 次 | 
| 最近记录: |