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解析器时,需要一个处理器来为各种解析器事件定义事件动作。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 [email protected]
*/
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("标签"+qName + ">解析结束");
}
/**
* @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,
标签调用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 [email protected]
*/
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;
}
}
运行结果: