我正在参与掘金创作者训练营第6期, 点击了解活动详情
1 概述
在上一个章节,简单的操作了下JAXP使用DOM解析XML,这一个章来简单说下Sax解析XML。在讲之前,我感觉多说一下什么我们为什么使用Sax来解析,使用原来的DOM解析器不好么?这里引入一个知识点——「CAP原则」。
简单来说,CAP原则又称CAP定理,指的是在一个分布式系统中,有三种属性原则,他们分别是
- 一致性(Consistency)
- 可用性(Availability)
- 分区容错性(Partition tolerance)。
首字母组合之后就是CAP原则,CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。有兴趣的可以查下,这里不在做过多的表述。
JAXP(Java AOI for XML Processing)是 SUN 公司推出的解析标准实现,这个工具提供了两种解析器方式来解析XML。其中一种就是本文所讲的「Sax解析器」。SAX解析器被称作SAXParser,SAXParser是由javax.xml.parsers.SAXParserFactory创建的。与上一章节的DOM不同,Sax不需要在内存解释,所以速度上比较快,而且Sax是通过w3c执行标准org.xml.sax.helpers.DefaultHandler事件回调来实现查找元素。
2 JAXP-SAX解析
XML是一种通用的数据交换格式,它的平台无关,语言无关,系统无关,在不同的语言环境的解析方式都是一样的,只不过是实现的语法不同。
SAX解析方式会逐行地去扫描XML文档,当遇到标签时会触发解析处理器,采用事件处理的方式解析XML (Simple API for XML) ,不是官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它。
SAX解析可分四个步骤进行:
1、实例化SAX解析工厂(SAXParserFactory),创建SAX解析器
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
2、由解析器解析得到xml文件对应的资源,可以是xml的输入流,文件和uri,从图中提供的API我们可以看出来需要提供的参数
saxParser.parse(new File(xmlPath), new CustomHandler());
其中自定义类CustomHandler()继承了DefaultHandler的 implements EntityResolver, DTDHandler, ContentHandler, ErrorHandler实现。而我们在使用过程中,实现最多的要数ContentHandler这个类中的实现。主要涉及到的方法有
-
characters (char ch[], int start, int length)
-
startDocument ()
-
endDocument()
-
startElement (String uri, String localName, String qName, Attributes atts)
-
endElement (String uri, String localName, String qName)
3、由用户自定义实现来处理XML
Sax解析的流程图如下:
2.1 创建实验类SaxParserXmlTest,创建xml文件
首先创建一个类SaxParserXmlTest.java,然后创建一个persons.xml,xml可以使用上一章节我们用过的。
public static void main(String[] args) {
String xmlPath = "./tool-jdk8/src/main/java/com/liuyc/tooljdk/xml/persons.xml";
try {
// 1、创建解析器工厂,创建解析器
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
// 2、执行解析转换操作
saxParser.parse(new File(xmlPath), new CustomHandler());
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
2.2 编写CustomHandler继承DefaultHandler
该类是SAX解析的核心所在,我们要重写以下几个我们关心的方法。
1、startDocument () :文档解析开始时调用,该方法只会调用一次(可以省略)
2、startElement (String uri, String localName, String qName, Attributes attributes)
3、characters (char[] ch, int start, int length)
4、endElement (String uri, String localName, String qName)
5、endDocument () :文档解析结束后调用,该方法只会调用一次(可以省略)
class CustomHandler extends DefaultHandler {
/**
* 元素个数
*/
public int sumElement = 0;
/**
* 元素个数【person】
*/
public int sumPerson = 0;
@Override
public void startDocument() throws SAXException {
System.out.println("开始解析文档==>");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
// 拼接标签的属性
if (attributes != null && attributes.getLength() > 0) {
String attrs = "";
for (int i = 0; i < attributes.getLength(); i++) {
String attrName = attributes.getQName(i);
String value = attributes.getValue(i);
attrs += " " + attrName + "="" + value + """;
}
System.out.print("<" + qName + attrs + ">");
}
// 标签没有属性,直接输出
else {
System.out.print("<" + qName + ">");
}
if("person".equals(qName)) {
sumPerson ++;
}
sumElement ++;
}
@Override
public void endElement(String uri, String localName, String qName) {
System.out.print("</" + qName + ">");
}
@Override
public void characters(char[] ch, int start, int length) {
System.out.print(new String(ch, start, length));
}
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
System.out.println("[prefix]" + prefix + "[uri]" + uri);
}
@Override
public void endDocument() throws SAXException {
System.out.println();
System.out.println("完成解析文档==>");
System.out.println("共有" + sumElement + "个元素,其中【person】元素有" + sumPerson + "个");
}
}
上面参数属性
- uri : 名称空间 URI ,如果元素没有则为空字符串。
- localName : 本地名称,如果没有名称空间,则为空字符串。
- qName : 限定的名称,可以理解为就是标签,例如
<person></person>,person就是qName,当然这里没有值。 - attributes: 附加到元素的属性,可以理解为标签上的属性对,有名称(qName)和值(value),例如
uid="QS#123",uid就是qName,QS#123就是value。 - characters :文本
2.3 解析结果
sax方式解析XML文档结束,打印如下结果。
开始解析文档==>
<persons>
<person sid="001" uid="QS#123">
<name>张小帅</name>
<sex>男</sex>
<age>28</age>
</person>
<person sid="002">
<name>刘晓萌</name>
<sex>女</sex>
<age>21</age>
</person>
<person sid="003">
<name>王老四</name>
<sex>男</sex>
<age>38</age>
</person>
</persons>
完成解析文档==>
共有13个元素,其中【person】元素有3个
可以看出来,SAX是一行一行处理。
2.4 总结
SAX解析XML具有解析速度快,占用内存少,对于Android等移动设备来说有巨大的优势,深入了解SAX的事件触发机制是掌握SAX解析的关键,掌握了SAX的事件触发就掌握了SAX解析XML。