Java基础之SAX解析XML

1. Java解析XML简介

Java库中提供了两种XML解析器:

像文档对象模型(Document Object Model,DOM)解析器这的树型解析器(tree parse),它们将读入的XML文档转换成树结构。

像XML简单API(Simple API for XML,SAX)解析器这样的流机制解析器(streaming parser),它们在读入XML文档时生成相应的事件。

2. SAX简介

SAX解析器在解析XML输入数据的各个组成部分时会报告事件,但不会以任何方式存储文档,而是由事件处理器建立相应的数据结构。实际上DOM解析器是在SAX解析器的基础上构建的,它在接收到解析器事件时构建DOM树。

3. 使用SAX解析器

SAX解析器架构图

在使用SAX解析器时,需要一个处理器来为各种解析器事件定义事件动作。DefaultHandler接口定义了若干个在解析文档时解析器会调用的回调方法。下面是最重要的几个方法:

startElement和endElement在每当遇到起始或终止标签时调用。

characters在每当遇到字符数据时调用。

startDocument和endDocument分别在文档开始和结束时各调用一次。

例如,在解析以下片段时:

韩信

25

解析器会产生以下回调:

1)startElement,元素名:person

2)startElement,元素名:name ,属性:type="string"

3)characters,内容:韩信

4)endElement,元素名:name

5)startElement,元素名:age

6)characters,内容:25

7)endElement,元素名:age

8)endElement,元素名:person

处理器必须覆盖这些方法,让它们执行在解析文件时我们想让它们执行的动作。

下面通过一个简单的demo体会SAX解析XML的过程。

3.1 准备xml

首先准备person.xml,内容如下

韩信

25

李白

23

3.2 解析代码

获取解析工厂

从解析工厂获取解析器

得到解读器

设置内容处理器

读取xml的文档内容

package cn.lastwhisper.javabasic.Sax;

import org.xml.sax.Attributes;

import org.xml.sax.SAXException;

import org.xml.sax.XMLReader;

import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;

import javax.xml.parsers.SAXParser;

import javax.xml.parsers.SAXParserFactory;

import java.io.IOException;

/**

* @author lastwhisper

* @desc SAX方式解析xml文件

* @email gaojun56@163.com

*/

public class SaxTest {

public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {

// SAX解析

// 1.获取解析工厂

SAXParserFactory factory = SAXParserFactory.newInstance();

// 2.从解析工厂获取解析器

SAXParser parse = factory.newSAXParser();

// 3.得到解读器

XMLReader reader=parse.getXMLReader();

// 4.设置内容处理器

reader.setContentHandler(new PHandler());

// 5.读取xml的文档内容

reader.parse("src/main/java/cn/lastwhisper/javabasic/Sax/person.xml");

}

}

class PHandler extends DefaultHandler {

/**

* @author lastwhisper

* @desc 文档解析开始时调用,该方法只会调用一次

* @param

* @return void

*/

@Override

public void startDocument() throws SAXException {

System.out.println("----解析文档开始----");

}

/**

* @author lastwhisper

* @desc 每当遇到起始标签时调用

* @param uri xml文档的命名空间

* @param localName 标签的名字

* @param qName 带命名空间的标签的名字

* @param attributes 标签的属性集

* @return void

*/

@Override

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

System.out.println("标签<"+qName + ">解析开始");

}

/**

* @author lastwhisper

* @desc 解析标签内的内容的时候调用

* @param ch 当前读取到的TextNode(文本节点)的字节数组

* @param start 字节开始的位置,为0则读取全部

* @param length 当前TextNode的长度

* @return void

*/

@Override

public void characters(char[] ch, int start, int length) throws SAXException {

String contents = new String(ch, start, length).trim();

if (contents.length() > 0) {

System.out.println("内容为-->" + contents);

} else {

System.out.println("内容为-->" + "空");

}

}

/**

* @author lastwhisper

* @desc 每当遇到结束标签时调用

* @param uri xml文档的命名空间

* @param localName 标签的名字

* @param qName 带命名空间的标签的名字

* @return void

*/

@Override

public void endElement(String uri, String localName, String qName) throws SAXException {

System.out.println("标签解析结束");

}

/**

* @author lastwhisper

* @desc 文档解析结束后调用,该方法只会调用一次

* @param

* @return void

*/

@Override

public void endDocument() throws SAXException {

System.out.println("----解析文档结束----");

}

}

运行main函数,查看运行结果

运行结果

3.3 分析解析过程

----解析文档开始---- 调用startDocument

标签解析开始 调用startElement

内容为-->空 调用characters,因为之间只有标签没有内容,所以为空。

标签解析开始 调用startElement

内容为-->空 调用characters,因为之间只有标签没有内容,所以为空。

标签解析开始 调用startElement

内容为-->韩信 调用characters,韩信之间内容为:韩信

标签解析结束 调用endElement

内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters

标签解析开始 调用startElement

内容为-->25 调用characters,25之间内容为:25

标签解析结束 调用endElement

内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters

标签解析结束 调用endElement

内容为-->空 调用characters,每次执行完调用endElement之后会调用一次characters

...

标签解析开始

内容为-->空

标签解析开始

内容为-->李白

标签解析结束

内容为-->空

标签解析开始

内容为-->23

标签解析结束

内容为-->空

标签解析结束

内容为-->空

标签解析结束

----解析文档结束---- 调用endDocument,xml解析结束

4. 解析xml到pojo对象

待解析的xml,person.xml

韩信

25

李白

23

xml转换为pojo的代码

package cn.lastwhisper.javabasic.Sax;

import org.xml.sax.Attributes;

import org.xml.sax.SAXException;

import org.xml.sax.XMLReader;

import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;

import javax.xml.parsers.SAXParser;

import javax.xml.parsers.SAXParserFactory;

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

/**

* @author lastwhisper

* @desc 将xml数据解析到pojo中

*

* @email gaojun56@163.com

*/

public class SaxXmlToPojo {

public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {

// SAX解析

// 1.获取解析工厂

SAXParserFactory factory = SAXParserFactory.newInstance();

// 2.从解析工厂获取解析器

SAXParser parse = factory.newSAXParser();

// 3.得到解读器

XMLReader reader = parse.getXMLReader();

// 4.设置内容处理器

PersonHandler personHandler = new PersonHandler();

reader.setContentHandler(personHandler);

// 5.读取xml的文档内容

reader.parse("src/main/java/cn/lastwhisper/javabasic/Sax/person.xml");

List persons = personHandler.getPersons();

for (Person person : persons) {

System.out.println("姓名:" + person.getName() + " 年龄:" + person.getAge());

}

}

}

class PersonHandler extends DefaultHandler {

private List persons;

private Person person;

private String tag; // 存储操作标签

/**

* @author lastwhisper

* @desc 文档解析开始时调用,该方法只会调用一次

* @param

* @return void

*/

@Override

public void startDocument() throws SAXException {

persons = new ArrayList();

}

/**

* @author lastwhisper

* @desc 标签(节点)解析开始时调用

* @param uri xml文档的命名空间

* @param localName 标签的名字

* @param qName 带命名空间的标签的名字

* @param attributes 标签的属性集

* @return void

*/

@Override

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

tag = qName;

if ("person".equals(tag)) {

person = new Person();

}

}

/**

* @author lastwhisper

* @desc 解析标签的内容的时候调用

* @param ch 字符

* @param start 字符数组中的起始位置

* @param length 要从字符数组使用的字符数

* @return void

*/

@Override

public void characters(char[] ch, int start, int length) throws SAXException {

String contents = new String(ch, start, length).trim();

if ("name".equals(tag)) {

person.setName(contents);

} else if ("age".equals(tag)) {

if (contents.length() > 0) {

person.setAge(Integer.valueOf(contents));

}

}

}

/**

* @author lastwhisper

* @desc 标签(节点)解析结束后调用

* @param uri xml文档的命名空间

* @param localName 标签的名字

* @param qName 带命名空间的标签的名字

* @return void

*/

@Override

public void endElement(String uri, String localName, String qName) throws SAXException {

if ("person".equals(qName)) {

persons.add(person);

}

tag = null; //tag丢弃了

}

/**

* @author lastwhisper

* @desc 文档解析结束后调用,该方法只会调用一次

* @param

* @return void

*/

@Override

public void endDocument() throws SAXException {

}

public List getPersons() {

return persons;

}

}

运行结果:

运行结果