上一节写到了如何使用DOM解析XML文档,而且掌握DOM解析的关键就在于要理解树模型。可以发现,DOM解析的步骤非常繁琐,代码量繁多,但是只要明白了树结构模型,理解起来也不是很难。这一篇文章将讲解一种步骤较为简单,代码量简化了很多,但是理解难度有点大的解析方式——SAX解析。
首先,我们来了解下什么叫SAX。
SAX,即Simple API For XML。非W3C官方所提供的标准,是“民间”的事实标准。SAX在概念上与DOM完全不同。SAX是非文档驱动,而是事件驱动的。
所谓事件驱动,就是一种基于回调机制的程序运行方法。SAX解析器装载XML文件时,它遍历XML文档并在其主机应用程序中产生事件(经由回调函数、指派函数或者任何可调用平台完成这一功能)表示这一过程。
我们知道DOM解析的核心是将XML文档构建成一棵树的模型,而SAX解析方式的核心是要创建一个XML解析器类,让其继承于DefaultHandler类,并且重写DefaultHandler类的五个回调方法。那么我们就从五个回调方法讲起。
一、XML解析器DefaultHandler类的五个回调方法
(一)startDocument
该方法表示XML文档的开始,当被解析的XML文档调入解析器开始解析时调用该方法,该方法抛出一个SAXException异常。
(二)endDocument
该方法表示XML文档的结束,当被解析的XML文档解析完毕时调用该方法,该方法抛出一个SAXException异常。
(三)startElement
该方法表示元素开始,当一对标记中的起始标记处理后,解析器激发此事件,这里的元素也包括标记名和其属性。该方法接收4个参数(String uri, String localName, String qName, Attributes attributes),最后一个参数表示可以通过这个元素获得该元素的某一属性值,所以通常在这个方法里获得元素的属性值。同样,该方法抛出一个SAXException异常。
(四)endElement
该方法表示元素开始,当一对标记中的起始标记处理后,解析器激发此事件,这里的元素也包括标记名和其属性。该方法接收3个参数(String uri, String localName, String qName),往往通过这个方法来获取对应的元素(包括文本)名称。同样,该方法抛出一个SAXException异常。
(五)Characters
包含字符串数据,类似于DOM的一个Text节点。该方法接收3个参数(char[] ch, int start, int length),当碰到文本的时候会触发该方法,所以可以在这个方法里保存读取到的文本信息(通常使用一个成员变量保存),然后通过保存的文本信息传递到endElement方法中读取。同样,该方法抛出一个SAXException异常。
由此我们可以知道五个回调方法中最为核心的是后面三个。我们可以用一副图来理解一下SAX解析。
接下里,我们通过一个实际的例子来掌握下SAX解析。
二、SAX解析实例。
(一)先用Eclipse创建一个项目,也可以在之前DOM解析的项目下新建一个sax解析的包。
这里我们同样使用之前在DOM解析时用到的XML文件,即book.xml。(不知道的小伙伴可以在上一篇DOM解析中找到),同样需要创建一个与该XML文档对应的模型类——Book.java。内容与上一届DOM解析中的一模一样,这里不重复粘贴代码了。
(二)创建一个解析器类。具体要求如下:
1、让其继承于DefaultHandler类,并重写刚刚提到的五个回调方法;
2、创建一个Book对象保存读取到的元素结点,创建一个String变量,用来保存在Characters方法中读取到的文档内容;
3、创建一个List列表用来保存解析的Book对象,为其添加一个getter方法。
具体可参考以下代码:
public class DemoHandler extends DefaultHandler {
private ArrayList<Book> list = new ArrayList<>();
public ArrayList<Book> getList() {
return list;
}
private Book book;
private String text;
@Override
public void startDocument() throws SAXException {
// 当解析文档开始后执行
super.startDocument();
System.out.println("startDocument");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// 当碰到一个元素的时候执行
super.startElement(uri, localName, qName, attributes);
// 如果是book标签开头
if (qName.equals("book")) {
// 获得book的属性
book = new Book();
String isbn = attributes.getValue("isbn");
book.setIsbn(isbn);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
// 当碰到的元素标记结束后执行
super.endElement(uri, localName, qName);
// 结束了book内容,说明该对象的内容读完了
if (qName.equals("book")) {
// 将该对象放到list中去
list.add(book);
} else if (qName.equals("title")) {
book.setTitle(text);
} else if (qName.equals("author")) {
book.setAuthor(text);
} else if (qName.equals("price")) {
int price = Integer.parseInt(text);
book.setPrice(price);
}
}
@Override
public void endDocument() throws SAXException {
// 当要解析的文档结束后执行
super.endDocument();
System.out.println("endDocument");
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// 当碰到文本信息(非元素标签)的时候执行
super.characters(ch, start, length);
this.text = new String(ch, start, length);
}
}
(三)创建一个包含主方法的主类来测试这个SAX解析器,具体可参考以下代码:
public class ParseApp {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
// 创建SAX解析器对象
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
File f = new File("book.xml");
// 解析处理器
DemoHandler handler = new DemoHandler();
// 调用解析方法 f -----> handler
parser.parse(f, handler);
// 得到解析结果
ArrayList<Book> list = handler.getList();
// 打印解析到的数据
for (Book book : list) {
System.out.println(book);
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(四)运行结果截图
之后会更新一篇利用第三方包SimpleXML的方式去解析XML文档。
谢谢您的关注和阅读,文章不当之处还请您不吝赐教~~~