什么时候应该select通过StAX的SAX?

像SAX和StAX这样的stream式xmlparsing器比构build像DOMparsing器这样的树结构的parsing器更快,更有效。 SAX是一个推式parsing器,这意味着它是观察者模式的一个实例(也称为侦听器模式)。 SAX先在那里,但后来又到了StAX–一个拉parsing器,这意味着它基本上像迭代器一样工作。

您可以find为什么StAX比SAX更喜欢SAX的原因,但通常归结为:“使用更简单”。

在JAXP的Java教程中,StAX被隐约地表述为DOM和SAX之间的中间部分:“比SAX更容易,比DOM更高效”。 但是,我从来没有发现任何StAX比SAX更慢或更less内存效率的线索。

所有这些让我怀疑: 有什么理由selectSAX而不是StAX?

概括一下, 认为StAX可以和SAX一样高效。 随着StAX的改进devise, 不能真正发现SAXparsing是首选的情况,除非使用遗留代码。

编辑 :根据这个博客Java SAX与StAX StAX提供没有模式validation。

概观
XML文档是分层的文档,其中相同的元素名称和命名空间可能出现在几个地方,具有不同的含义,并且处于不确定的深度(recursion)。 正常情况下,解决大问题的办法是把它们分成小问题。 在XMLparsing的情况下,这意味着在特定于该XML的方法中parsingXML的特定部分。 例如,一个逻辑将parsing一个地址:

 <Address> <Street>Odins vei</Street> <Building>4</Building> <Door>b</Door> </Address> 

即你会有一个方法

 AddressType parseAddress(...); // A 

要么

 void parseAddress(...); // B 

在你的逻辑的某个地方,用XMLinput参数并返回一个对象(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 } 

在“构build”开始元素的情况下,您需要确定您实际上正在parsing一个地址,然后将XML事件路由到其解释地址的方法。

StAX的
StAX'拉''XML 事件 ,留给你决定你的程序/数据在哪里接收XML事件。

 // method in standard StAX reader int event = reader.next(); if(event == XMLStreamConstants.START_ELEMENT) { // .. your logic here for start element } 

当然,你总是希望在其解释地址的方法中接收到“构build”事件。

讨论
SAX和StAX的区别在于推拉。 在这两种情况下,parsing状态必须以某种方式处理。

这转化为SAX的典型方法B,StAX的方法A. 另外,SAX必须给出B个别的XML事件,而StAX可以给出多个事件(通过传递一个XMLStreamReader实例)。

因此,B首先检查parsing的先前状态,然后处理每个单独的XML事件,然后存储状态(在一个字段中)。 方法A可以通过多次访问XMLStreamReader来一次处理XML事件,直到满足为止。

结论
StAX允许您根据XML结构构buildparsing(数据绑定)代码 ; 所以对于SAX来说,StAX的程序stream中隐含着“状态”,而在SAX中,对于大多数事件调用,您始终需要保留某种状态variables+根据该状态对stream进行路由。

除了最简单的文档之外,我推荐使用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; } 

然后最终达到您将读取基本types的级别。

 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; } 

这很简单,没有误解的余地。 只要记得正确地减less水平:

A.在你期望的字符后,但在一些应该包含字符的标签(在上面的模式中)中有一个END_ELEMENT:

 <Name>Thomas</Name> 

而是

 <Name></Name> 

一个缺失的子树也是如此,你明白了。

B.调用开始元素上调用的子parsing方法后,返回AFTER对应的结束元素,即parsing器比方法调用之前(上述模式)低一级。

请注意,这种方法如何完全忽略“可忽略的”空白,以获得更强大的实现。

parsing器
与Woodstox的大部分function或Aaalto-xml的速度。

@Rinke:我想只有当你不需要处理/处理XML内容的时候,我认为SAX优于STAX。 因为只有你想要做的事情是检查传入的XML的格式,只是想处理错误,如果它…在这种情况下,你可以简单地调用SAXparsing器的parse()方法,并指定error handling程序来处理任何parsing问题….所以基本上STAX绝对是你想要处理内容的情况下,最好的select,因为SAX内容处理程序太难以编码…

这种情况的一个实际例子可能是,如果您的企业系统中有一系列SOAP节点,并且入门级SOAP节点只允许那些SOAP XML通过下一阶段的良好格式化,那么我看不到任何理由会使用STAX。 我只是使用SAX。

这完全是一个平衡。

你可以用一个阻塞队列和一些线程技巧把一个SAXparsing器变成一个pullparsing器,对我来说,和第一次看起来差不多。

我相信目前StAX需要通过第三方jar打包,而SAX在javax中免费。

我最近select了SAX,并在其周围build立了一个拉parsing器,所以我不需要依赖第三方的jar。

Java的未来版本几乎肯定会包含一个StAX实现,所以问题就消失了。

StAX使您能够创build快速的双向XMLparsing器。 它在性能和可用性方面certificate了其他方法(如DOM和SAX)的更好select

您可以在Java StAX教程中阅读有关StAX的更多信息

这些答案提供的大部分信息都已经过时了……在2013年的研究论文中已经对所有的XMLparsing库进行了全面的研究…读了它,你会很容易地看到明确的赢家(提示:只有一个真正的赢家)…

bitstream/10400.22/1847/1/ART_BrunoOliveira_2013.pdf