gpt4 book ai didi

java - 我什么时候应该选择 SAX 而不是 StAX?

转载 作者:IT老高 更新时间:2023-10-28 11:44:47 26 4
gpt4 key购买 nike

像 SAX 和 StAX 这样的流式 xml 解析器比构建像 DOM 解析器这样的树结构的解析器更快,内存效率更高。 SAX 是一个推送解析器,这意味着它是观察者模式(也称为监听器模式)的一个实例。 SAX 首先出现,但随后出现了 StAX - 一个拉式解析器,这意味着它基本上像迭代器一样工作。

您可以在任何地方找到为什么更喜欢 StAX 而不是 SAX 的原因,但通常归结为:“它更易于使用”。

在关于 JAXP 的 Java 教程中,StAX 被模糊地表示为 DOM 和 SAX 之间的中间:“它比 SAX 更容易,比 DOM 更高效”。但是,我从未发现任何迹象表明 StAX 会比 SAX 更慢或内存效率更低。

这一切让我想知道:有什么理由选择 SAX 而不是 StAX?

最佳答案

概述
XML 文档是分层文档,其中相同的元素名称和 namespace 可能出现在多个位置,具有不同的含义,并且具有不定式深度(递归)。通常,解决大问题的方法是将它们分成小问题。在 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 events ,由您决定 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
}

如果是“建筑”开始元素,您需要确定您实际上是在解析地址,然后将 XML 事件路由到负责解释地址的方法。

StAX
StAX“拉动”XML events ,由您决定在您的程序/数据中的哪个位置接收 XML 事件。

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

当然,您总是希望在解释地址的方法中接收“建筑”事件。

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

这转换为 SAX 的典型方法 B,以及 StAX 的方法 A。此外,SAX 必须给 B 单独的 XML 事件,而 StAX 可以给 A 多个事件(通过传递 XMLStreamReader 实例)。

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

结论
StAX 允许您根据 XML 结构构建解析(数据绑定(bind))代码;所以就 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速度。

关于java - 我什么时候应该选择 SAX 而不是 StAX?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7521803/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com