平时我们用解析Xml 文档有几种常用的方法SAX 的event 方式, DOM 的方式。 这个SAX 是基于事件模式效率性能方面还是不错的, 最大的缺陷是作为事件方式客户端没有太大控制权都是被回调EventHandler;
DOM 的方式在处理小XML文件的时候是很方便的可以方便的读写, 最大的问题是内存cpu 的资源占用方面太夸张了。 动不动outofmessage 实在是伤不起。
现在由于有需求不想采用SAX 事件方式, 我想的是在client 端有最大的控制权限,采用跟BufferReader 的方式readline类似的接口 比如 提供 hasNext(), getNext() 去获取一个XML 元素。 这个元素是基于XPath来指定。 通过反复的google,找到点线索, 在2004年 通过了一个 JSR 173 关于StAX 的东东。 The primary goal of the StAX API is to give "parsing control to the programmer by exposing a simple iterator based API. 这个不就是俺想要的东东么。
我们可以通过StAX 提供的几个接口来遍历xml stream 有两个接口可以干这个事情:
XMLStreamReader
XMLEventReader
比如XMLStreamReader 的用法可以如下:
XMLInputFactory f = XMLInputFactory.newInstance();
XMLStreamReader r = f.createXMLStreamReader( ... );
while(r.hasNext()) {
r.next();
}
XMLEventReader 的用法
while(stream.hasNext()) {
XMLEvent event = stream.nextEvent();
System.out.print(event);
}
我们可以通过这种方式来遍历xml 文档, 通过返回的XMLEvent 来决定是否StartElement ,EndElement 来决定当前的Element 是否我们想要的。 下面是我写的一个XMLMessageSource 类
1, 它接受 fileName xml 文档
2, path XPath 我们需要的元素的XPath
它实现了 hasNext, getNextMessage 方法来访问下一个满足条件的Element 的String 内容
package com.bwang.messagefeeder.messagesource;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
@SuppressWarnings("restriction")
public class XmlMessageSource implements IMessageSource {
private static final Logger LOGGER = Logger
.getLogger(XmlMessageSource.class);
private final String fileName;
private final String xmlPathPattern;
private final String[] patternArray;
private XMLEventReader eventReader;
private final Stack<String> elementNames;
public XmlMessageSource(FileEntry fileEntry, String xmlPathPattern) {
this.fileName = fileEntry.getFileName();
this.xmlPathPattern = xmlPathPattern;
patternArray = makePatternArray();
if (patternArray == null || patternArray.length ==0) {
throw new RuntimeException("pattern is not set");
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(patternArray);
}
elementNames = new Stack<String>();
try {
XMLInputFactory xif = XMLInputFactory.newInstance();
eventReader = xif.createXMLEventReader(new FileReader(this.fileName));
} catch (Exception e) {
LOGGER.error("fail to create event reader", e);
}
}
private String[] makePatternArray() {
String[] tempArray = this.xmlPathPattern.split("/");
List<String> patternStrings = new ArrayList<String>();
for (int i=0; i< tempArray.length; i++) {
if (!StringUtils.isEmpty(tempArray[i])) {
patternStrings.add(tempArray[i]);
}
}
return patternStrings.toArray(new String[0]);
}
public boolean hasNext() {
try {
while (eventReader.hasNext()) {
XMLEvent xmlEvent = eventReader.peek();
if (xmlEvent.isStartElement()) {
StartElement elem = xmlEvent.asStartElement();
String name = elem.getName().getLocalPart();
elementNames.push(name);
}
if (match()) {
elementNames.pop();
return true;
}
else if (xmlEvent.isEndElement()) {
elementNames.pop();
}
xmlEvent = eventReader.nextEvent();
}
} catch (Exception e) {
}
return false;
}
private boolean match() {
if (elementNames.size() != patternArray.length) {
return false;
}
for(int i=0; i< elementNames.size(); i++) {
if (!elementNames.get(i).equalsIgnoreCase(patternArray[i])) {
return false;
}
}
return true;
}
public String getNextMessage() {
try {
return readElementBody(eventReader);
} catch (Exception e) {
LOGGER.error("fail to get message", e);
return null;
}
}
public static String readElementBody(XMLEventReader eventReader)
throws XMLStreamException {
StringWriter buf = new StringWriter(1024);
int depth = 0;
do {
// peek event
XMLEvent xmlEvent = eventReader.peek();
if (xmlEvent.isStartElement()) {
++depth;
} else if (xmlEvent.isEndElement()) {
--depth;
}
// consume event
xmlEvent = eventReader.nextEvent();
// print out event
xmlEvent.writeAsEncodedUnicode(buf);
} while (depth > 0 && eventReader.hasNext());
return buf.getBuffer().toString();
}
public void dispose() {
if (eventReader != null) {
try {
eventReader.close();
}catch(XMLStreamException e) {
}
}
}
}
hasNext 会将eventReader 的游标停留在满足条件的Element 的StartElement 处。
调用方法是:
IMessageSource ms = new XmlMessageSource(new fileentry(), "/DOCS/Message");
while(ms.hasNext()) {
System.out.println(ms.getNextMessage());
}
ms.dispose();
通过一个大约200多M 的测试文档结构N轮的测试下来
SAX Event 方式 大约 240 秒
这个实现 大约 20秒钟不到。
DOM 实现就表提了,直接内存爆掉。
终于找到一种性能秒杀SAX 的实现, 比较这个实现在测试的过程中内存占用文档在 30M 不到。
分享到:
相关推荐
Stax组装及解析XML的例子。 Stax组装及解析XML的例子。
Stax组装及解析XML的例子。Stax组装及解析XML的例子。
kettle 解析xml数据,xml多层分组嵌套,xml stax方法,完整解析案例使用(包含xml文件以及ktr文件)。ETL大数据迁移,数据清洗。XML Input Stream (StAX) 方法
java Stax 解析xml
使用StAX进行高效的XML处理中文版 强烈推荐
与上一个版本一起使用可以可以起到提高效率的目的。本人解析xml的真实文档
使用woodstax+jaxb进行xml的流解析,包括解析类,解析文件,所需jar包,带注解的实体类。提高了解析效率,减少了内存消耗。
java使用stax技术操作XML文档.doc
stax 以流事件的方式读取xml,可以应用与大型的xml文档的操作,使用最新的stax xml解析技术读写xml的eclipse 工程
java 解析xml 方法 sax jdom dom stAX
kettle转换xml(XML Input Stream (StAX))实例,包含xml和ktr
xml解析 SAX解析 XML pull解析 DOM解析 包含三种解析方式 全部实现xml pull与stax类似 故省略
stax-api-1.0.1 java 操作 xml 文件 一个很好用的包 111
解析xml解析xml解析xml解析xml解析xml解析xml解析xml解析xml
Pull based(pull模式),OM基于StAX--标准的pull parser API 。 pull模式 Axiom采用pull解析方式,基于StAX(JSR173)。 SAX和DOM 都是基于push的解析方式,也就是说解析控制在parser本身。 Axiom和StAX紧密...
Stax2 API是标准 API(“用于Xml处理的STandard Api”)的扩展,它是JDK 6中添加的JDK的pull-parser API。 地位 支持 Stax2 API通过以下Stax XML实现来本地实现: (面向性能,也是非阻塞/异步的) (Java平台上...
NULL 博文链接:https://zangweiren.iteye.com/blog/647334
使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。 双语对照,边学技术、边学英语。
解析xml java与xml转换的demo --stax jaxb