dom4j介绍
dom4j的项目地址:http://sourceforge.net/projects/dom4j/?source=directory
dom4j是一个简单的开源库,用于处理XML、 XPath和XSLT,它基于Java平台,使用Java的集合框架,全面集成了DOM,SAX和JAXP。
dom4j的使用
下载了dom4j项目之后,解压缩,将其jar包(我的当前版本叫做dom4j-1.6.1.jar)加入class path下面。
(Properties->Java Build Path -> Add External JARs...)。
之后就可以使用其提供的API进行编程。
程序实例1
第一个程序,用Java代码生成xml文档,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
package com.example.xml.dom4j;
import java.io.FileOutputStream;
import java.io.FileWriter;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
/**
* dom4j框架学习 使用dom4j框架创建xml文档并输出保存
*
*/
public class Dom4JTest1
{
public static void main(String[] args) throws Exception
{
// 第一种方式:创建文档,并创建根元素
// 创建文档:使用了一个Helper类
Document document = DocumentHelper.createDocument();
// 创建根节点并添加进文档
Element root = DocumentHelper.createElement( "student" );
document.setRootElement(root);
// 第二种方式:创建文档并设置文档的根元素节点
Element root2 = DocumentHelper.createElement( "student" );
Document document2 = DocumentHelper.createDocument(root2);
// 添加属性
root2.addAttribute( "name" , "zhangsan" );
// 添加子节点:add之后就返回这个元素
Element helloElement = root2.addElement( "hello" );
Element worldElement = root2.addElement( "world" );
helloElement.setText( "hello Text" );
worldElement.setText( "world text" );
// 输出
// 输出到控制台
XMLWriter xmlWriter = new XMLWriter();
xmlWriter.write(document);
// 输出到文件
// 格式
OutputFormat format = new OutputFormat( " " , true ); // 设置缩进为4个空格,并且另起一行为true
XMLWriter xmlWriter2 = new XMLWriter(
new FileOutputStream( "student.xml" ), format);
xmlWriter2.write(document2);
// 另一种输出方式,记得要调用flush()方法,否则输出的文件中显示空白
XMLWriter xmlWriter3 = new XMLWriter( new FileWriter( "student2.xml" ),
format);
xmlWriter3.write(document2);
xmlWriter3.flush();
// close()方法也可以
}
}
|
程序Console输出:
1
2
|
<? xml version = "1.0" encoding = "UTF-8" ?>
< student />
|
生成的一个xml文档:
1
2
3
4
5
6
|
<? xml version = "1.0" encoding = "UTF-8" ?>
< student name = "zhangsan" >
< hello >hello Text</ hello >
< world >world text</ world >
</ student >
|
程序实例2
程序实例2,读入xml文档并分析,将其内容输出。
首先,待分析的文档如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
<?xml version= "1.0" encoding= "UTF-8" ?>
<students name= "zhangsan" >
<hello name= "lisi" >hello Text1</hello>
<hello name= "lisi2" >hello Text2</hello>
<hello name= "lisi3" >hello Text3</hello>
<world name= "wangwu" >world text1</world>
<world name= "wangwu2" >world text2</world>
<world >world text3</world>
</students>
package com.example.xml.dom4j;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.DOMReader;
import org.dom4j.io.SAXReader;
/**
* dom4j框架学习: 读取并解析xml
*
*
*/
public class Dom4JTest2
{
public static void main(String[] args) throws Exception
{
SAXReader saxReader = new SAXReader();
Document document = saxReader.read( new File( "students.xml" ));
// 获取根元素
Element root = document.getRootElement();
System.out.println( "Root: " + root.getName());
// 获取所有子元素
List<Element> childList = root.elements();
System.out.println( "total child count: " + childList.size());
// 获取特定名称的子元素
List<Element> childList2 = root.elements( "hello" );
System.out.println( "hello child: " + childList2.size());
// 获取名字为指定名称的第一个子元素
Element firstWorldElement = root.element( "world" );
// 输出其属性
System.out.println( "first World Attr: "
+ firstWorldElement.attribute( 0 ).getName() + "="
+ firstWorldElement.attributeValue( "name" ));
System.out.println( "迭代输出-----------------------" );
// 迭代输出
for (Iterator iter = root.elementIterator(); iter.hasNext();)
{
Element e = (Element) iter.next();
System.out.println(e.attributeValue( "name" ));
}
System.out.println( "用DOMReader-----------------------" );
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
// 注意要用完整类名
org.w3c.dom.Document document2 = db.parse( new File( "students.xml " ));
DOMReader domReader = new DOMReader();
// 将JAXP的Document转换为dom4j的Document
Document document3 = domReader.read(document2);
Element rootElement = document3.getRootElement();
System.out.println( "Root: " + rootElement.getName());
}
}
|
代码运行后输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Root: students
total child count: 6
hello child: 3
first World Attr: name=wangwu
迭代输出-----------------------
lisi
lisi2
lisi3
wangwu
wangwu2
null
用DOMReader-----------------------
Root: students
|
SAX解析XML
下面是SAX实现实体解析的步骤
//下面使用XMLReader 来解析
(一)第一步:新建一个工厂类SAXParserFactory,代码如下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:让工厂类产生一个SAX的解析类SAXParser,代码如下:
SAXParser parser = factory.newSAXParser();
(三)第三步:从SAXPsrser中得到一个XMLReader实例,代码如下:
XMLReader reader = parser.getXMLReader();
(四)第四步:把自己写的handler注册到XMLReader中,一般最重要的就是ContentHandler,代码如下:
reader.setContentHandler(this);
(五)第五步:将一个xml文档或者资源变成一个java可以处理的InputStream流后,解析正式开始,代码如下:
reader.parse(new InputSource(is));
//下面使用SAXParser来解析
(一)第一步:新建一个工厂类SAXParserFactory,代码如下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:让工厂类产生一个SAX的解析类SAXParser,代码如下:
SAXParser parser = factory.newSAXParser();
(三)第三步:将一个xml文档或者资源变成一个java可以处理的InputStream流后,解析正式开始,代码如下:
parser.parse(is,this);
估计大家都看到了ContentHandler ,下面具体的讲下
解析开始之前,需要向XMLReader/SAXParser 注册一个ContentHandler,也就是相当于一个事件监听器,在ContentHandler中定义了很多方法
//设置一个可以定位文档内容事件发生位置的定位器对象
public void setDocumentLocator(Locator locator)
//用于处理文档解析开始事件
public void startDocument()throws SAXException
//处理元素开始事件,从参数中可以获得元素所在名称空间的uri,元素名称,属性类表等信息
public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException
//处理元素结束事件,从参数中可以获得元素所在名称空间的uri,元素名称等信息
public void endElement(String namespacesURI , String localName , String qName) throws SAXException
//处理元素的字符内容,从参数中可以获得内容
public void characters(char[] ch , int start , int length) throws SAXException
顺便介绍下XMLReader中的方法。
//注册处理XML文档解析事件ContentHandler
public void setContentHandler(ContentHandler handler)
//开始解析一个XML文档
public void parse(InputSorce input) throws SAXException
大概的讲的差不多了 接下来开始讲解解析的步骤
我们还是用上一章的代码
首先 我们创建一个Person类 用来存储用户的信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package com.example.demo;
import java.io.Serializable;
public class Person implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String _id;
private String _name;
private String _age;
public String get_id() {
return _id;
}
public void set_id(String _id) {
this ._id = _id;
}
public String get_name() {
return _name;
}
public void set_name(String _name) {
this ._name = _name;
}
public String get_age() {
return _age;
}
public void set_age(String _age) {
this ._age = _age;
}
}
|
接下来 我们要实现一个ContentHandler 用来解析XML
实现一个ContentHandler 一般需要下面几个步骤
1、声明一个类,继承DefaultHandler。DefaultHandler是一个基类,这个类里面简单实现了一个ContentHandler。我们只需要重写里面的方法即可。
2、重写 startDocument() 和 endDocument(),一般将正式解析之前的初始化放到startDocument()里面,收尾的工作放到endDocument()里面。
3、重写startElement(),XML解析器遇到XML里面的tag时就会调用这个函数。经常在这个函数内是通过对localName的值进行判断而操作一些数据。
4、重写characters()方法,这是一个回调方法。解析器执行完startElement()后,解析节点的内容后就会执行这个方法,并且参数ch[]就是节点的内容。
5、重写endElement()方法,这个方法与startElement()相对应,解析完一个tag节点后,执行这个方法,解析一个tag后,调用这个处理还原和清除相关信息
首先 新建一个类 继承DefaultHandler 并重写以下几个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public class SAX_parserXML extends DefaultHandler {
/**
* 当开始解析xml文件的声明的时候就会触发这个事件, 可以做一些初始化的工作
* */
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super .startDocument();
}
/**
* 当开始解析元素的开始标签的时候,就会触发这个事件
* */
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
super .startElement(uri, localName, qName, attributes);
}
/**
* 当读到文本元素的时候要触发这个事件.
* */
@Override
public void characters( char [] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
super .characters(ch, start, length);
}
/**
* 当读到结束标签的时候 就会触发这个事件
* */
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
super .endElement(uri, localName, qName);
}
}
|
首先 我们创建一个list 用来保存解析出来的person数据
1
|
List<Person> persons;
|
但是?在哪里初始化呢?我们可以在startDocument()里面初始化,因为当开始解析xml文件的声明的时候就会触发这个事件所以放在这里比较合适
1
2
3
4
5
6
7
8
9
10
|
/**
* 当开始解析xml文件的声明的时候就会触发这个事件, 可以做一些初始化的工作
* */
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super .startDocument();
// 初始化list
persons = new ArrayList<Person>();
}
|
接下来 就要开始解析了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/**
* 当开始解析元素的开始标签的时候,就会触发这个事件
* */
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
super .startElement(uri, localName, qName, attributes);
// 如果读到是person标签 开始存储
if (localName.equals( "person" )) {
person = new Person();
person.set_id(attributes.getValue( "id" ));
}
curNode = localName;
}
|
上面的代码中 localName表示当前解析到的元素名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
//步骤
//1.判断是否是person元素
//2.创建新的Person对象
//3.获取id 添加到Person对象中
curNode 用来保存当前的元素名 在characters中会使用到
/**
* 当读到文本元素的时候要触发这个事件.
* */
@Override
public void characters( char [] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
super .characters(ch, start, length);
if (person != null ) {
//取出目前元素对应的值
String txt = new String(ch, start, length);
//判断元素是否是name
if (curNode.equals( "name" )) {
//将取出的值添加到person对象
person.set_name(txt);
} else if (curNode.equals( "age" )) {
person.set_age(txt);
}
}
}
|
接下来是介绍标签结束的时候需要做的事情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 当读到结束标签的时候 就会触发这个事件
* */
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
super .endElement(uri, localName, qName);
// 如果是 并且person不为空,添加到list
if (localName.equals( "person" ) && person != null ) {
persons.add(person);
person = null ;
}
curNode = "" ;
}
|
解析的事情结束了 大概流程就是
1.一个元素开始时 会调用startElement方法
2.接下来会调用到characters方法,可以用来获取元素的值
3.一个元素结束时 会调用到endElement方法
解析结束之后 我们需要写一个方法 用来获取解析后保存的list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public List<Person> ReadXML(InputStream is) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser parser = factory.newSAXParser();
// 第一种方法
// parser.parse(is, this);
// 第二种方法
XMLReader reader = parser.getXMLReader();
reader.setContentHandler( this );
reader.parse( new InputSource(is));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return persons;
}
|
上面的代码就不解释了 只要将inputStream对象传入 就可以解析出内容
看完了代码,我来给出完整的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
package com.example.demo.Utils;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import com.example.demo.Person;
public class SAX_parserXML extends DefaultHandler {
List<Person> persons;
Person person;
// 当前节点
String curNode;
public List<Person> ReadXML(InputStream is) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser parser = factory.newSAXParser();
// 第一种方法
// parser.parse(is, this);
// 第二种方法
XMLReader reader = parser.getXMLReader();
reader.setContentHandler( this );
reader.parse( new InputSource(is));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return persons;
}
/**
* 当开始解析xml文件的声明的时候就会触发这个事件, 可以做一些初始化的工作
* */
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super .startDocument();
// 初始化list
persons = new ArrayList<Person>();
}
/**
* 当开始解析元素的开始标签的时候,就会触发这个事件
* */
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
super .startElement(uri, localName, qName, attributes);
// 如果读到是person标签 开始存储
if (localName.equals( "person" )) {
person = new Person();
person.set_id(attributes.getValue( "id" ));
}
curNode = localName;
}
/**
* 当读到文本元素的时候要触发这个事件.
* */
@Override
public void characters( char [] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
super .characters(ch, start, length);
if (person != null ) {
// 取出目前元素对应的值
String txt = new String(ch, start, length);
// 判断元素是否是name
if (curNode.equals( "name" )) {
// 将取出的值添加到person对象
person.set_name(txt);
} else if (curNode.equals( "age" )) {
person.set_age(txt);
}
}
}
/**
* 当读到结束标签的时候 就会触发这个事件
* */
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
super .endElement(uri, localName, qName);
// 如果是person结尾 并且person不为空,添加到list
if (localName.equals( "person" ) && person != null ) {
persons.add(person);
person = null ;
}
curNode = "" ;
}
}
|
写个方法调用下这个类
1
2
3
4
5
6
7
8
9
|
List<Person> persons = new SAX_parserXML().ReadXML(is);
StringBuffer buffer = new StringBuffer();
for ( int i = 0 ; i < persons.size(); i++) {
Person person =persons.get(i);
buffer.append( "id:" + person.get_id() + " " );
buffer.append( "name:" + person.get_name() + " " );
buffer.append( "age:" + person.get_age() + "\n" );
}
Toast.makeText(activity, buffer, Toast.LENGTH_LONG).show();
|
如果你看到下面的界面 说明解析成功了~
小结:
DOM(文件对象模型)解析:解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以根据DOM接口来操作这个树结构了。
优点:整个文档读入内存,方便操作:支持修改、删除和重现排列等多种功能。
缺点:将整个文档读入内存中,保留了过多的不需要的节点,浪费内存和空间。
使用场合:一旦读入文档,还需要多次对文档进行操作,并且在硬件资源充足的情况下(内存,CPU)。
为了解决DOM解析存在的问题,就出现了SAX解析。其特点为:
优点:不用实现调入整个文档,占用资源少。尤其在嵌入式环境中,如android,极力推荐使用SAX解析。
缺点:不像DOM解析一样将文档长期驻留在内存中,数据不是持久的。如果事件过后没有保存数据,数据就会丢失。
使用场合:机器有性能限制