解析XML文件的那些事

Java 原生解析

在Java 中,原生解析XML 文档的方式有两种,分别是:

  • Dom 解析
  • Sax 解析

Dom 解析XML

简介

Dom 解析功能强大,可增删改查,操作时会将XML 文档以文档对象的方式读取到内存中,因此适用于小文档。

优点

Document 对象代表了一个XML 文档的模型树(类似于数据结构中的),所有的其他Node 都以一定的顺序包含在Document 对象之内,排列成一个树状结构,以后对XML 文档的所有操作都与解析器无关,直接在这个Document 对象上进行操作即可。

代码

解析代码如下:

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
package com.vgbh;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class DomXMLDemo {

public static void main(String[] args) {

DomXMLDemo dxd = new DomXMLDemo();

String uri = "bookstore.xml";
String uri1 = "F:/WorkSpace/domB.xml";

//dxd.insertDomXMLDemo(uri1, "一生有你", "午歌", "25", "chinese", "china");//当XML文件中有数据时,该方法会进行覆盖。

//dxd.queryDomXMLDemo(uri);

dxd.updateDomXMLDemo(uri, "0", "changedName");
}

//遍历XML文件
public void queryDomXMLDemo (String uri) {
try {
//创建DOM解析器的工厂实例
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
//从DOM工厂中获取DOM解析器
DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();

//将解析文件传入DOM解析器
Document doc = dbBuilder.parse(uri);
//System.out.println("处理文件的DomImplenementation对象 = " + doc.getImplementation());//获取处理此文档的DOMImplenementation对象
//获取文档名称为book的元素的节点列表
NodeList bookList = doc.getElementsByTagName("book");

//遍历该集合
for (int i = 0 ;i < bookList.getLength();i ++) {
System.out.println("----------开始第" + (i+1) + "遍历。-------------");
Element e = (Element)bookList.item(i);
System.out.println("name:" + e.getElementsByTagName("name").item(0).getFirstChild().getNodeValue());
System.out.println("author:" + e.getElementsByTagName("author").item(0).getFirstChild().getNodeValue());
System.out.println("price:" + e.getElementsByTagName("price").item(0).getFirstChild().getNodeValue());
System.out.println("language:" + e.getElementsByTagName("language").item(0).getFirstChild().getNodeValue());
System.out.println("country:" + e.getElementsByTagName("country").item(0).getFirstChild().getNodeValue());
System.out.println("----------第" + (i+1) + "遍历结束。--------------");

/*
* 对于特定的方法可以查看API文档
* getElementsByTagName() 按文档数序返回具有特定标记的NodeList
* item() 返回集合中的第index个顶
* getFirstChild()
* getNodeValue()
*/
}

} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

//写入XML文件,该文件为新的文件
public void insertDomXMLDemo (String uri, String names, String authors, String prices, String languages, String countrys) {
Document doc ;
Element bookstore , book ;
Element name = null ;
Element author = null ;
Element price = null ;
Element language = null ;
Element country = null ;

try {
//创建DOM解析器的工厂实例
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
//从DOM工厂中获取DOM解析器
DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();

//创建文档树模型对象
doc = dbBuilder.newDocument();
if (doc != null) {
//创建bookstore元素
bookstore = doc.createElement("booksore");
//创建book元素
book = doc.createElement("book");

//设置book的id属性值,并且设置name, author, price, language, country
book.setAttribute("id", "3");
name = doc.createElement("name");
language = doc.createElement("language");
author = doc.createElement("author");
price = doc.createElement("price");
country = doc.createElement("country");

//设置name子节点的属性值
name.appendChild(doc.createTextNode(names));
author.appendChild(doc.createTextNode(authors));
price.appendChild(doc.createTextNode(prices));
language.appendChild(doc.createTextNode(languages));
country.appendChild(doc.createTextNode(countrys));

//将name, author, price, language, country添加到book中
book.appendChild(name);
book.appendChild(author);
book.appendChild(price);
book.appendChild(language);
book.appendChild(country);

//将book作为子元素加入bookstore
bookstore.appendChild(book);
//将bookstore作为子元素加入doc
doc.appendChild(bookstore);

//doc输出为XML文件
//创建TransformerFactory解析器的工厂实例
TransformerFactory tfFactory = TransformerFactory.newInstance();
//从TransFormer工厂中获取TransFormer解析器
Transformer tf = tfFactory.newTransformer();
//设置输出时自动换行
tf.setOutputProperty(OutputKeys.INDENT, "true");
//使用transFormer的transform()函数将Dom转换为XML文件
tf.transform(new DOMSource(doc), new StreamResult(uri));

System.out.println("生成XML文件成功!");
}

} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}

//更新XML文件
public void updateDomXMLDemo (String uri, String id, String name) {

/*
* 更新文件:
* 1、更新文件是在原有XML文件的基础上,获取到每一个元素book,
* 2、在通过相应的localName获取到他的Element对象,
* 3、最终通过修改这个Element对象实现完成修改,
* 4、还需要将其修改后的值重新覆盖掉原来的数据。
*
* 还有一种情况,那就是添加一整条数据,方法类似写入XML文件,最后还需要输出XML文件
*/

try {
//创建DOM解析器的工厂实例
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
//从DOM工厂中获取DOM解析器
DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();
//创建文档树模型对象
Document doc = dbBuilder.parse(uri);
//获取文档名称为book的元素的节点列表
NodeList bookList = doc.getElementsByTagName("book");

for (int i = 0; i < bookList.getLength(); i++) {
if (String.valueOf(i).equals(id)) {
Element e = (Element) bookList.item(i);
e.getElementsByTagName("name").item(0).getFirstChild().setNodeValue(name);
//System.out.println(" " + e.getElementsByTagName("name").item(0).getFirstChild().getNodeValue());
break;
}
}

//doc输出为XML文件
//创建TransformerFactory解析器的工厂实例
TransformerFactory tfFactory = TransformerFactory.newInstance();
//从TransFormer工厂中获取TransFormer解析器
Transformer tf = tfFactory.newTransformer();
//设置输出时自动换行
tf.setOutputProperty(OutputKeys.INDENT, "true");
//使用transFormer的transform()函数将Dom转换为XML文件
tf.transform(new DOMSource(doc), new StreamResult(uri));

System.out.println("生成XML文件成功!");

} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}

}
}

输出样例
  1. queryDomXMLDemo的输出结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-----------------开始第1遍历。----------------------
name:cet4
author:college-English
price:55
language:english
country:2
-----------------第1遍历结束。----------------------
-----------------开始第2遍历。----------------------
name:cet6
author:me
price:33
language:lalalal
country:chinese
-----------------第2遍历结束。----------------------

从这里可以看出,Dom 遍历XML 文件是追条进行遍历,所以只是用较小的XML 文件,并且这样可以修改其中的值,完成数据更新。

  1. insertDomXMLDemo 的输出结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <booksore>
    <book id="3">
    <name>一生有你</name>
    <author>午歌</author>
    <price>25</price>
    <language>chinese</language>
    <country>china</country>
    </book>
    </booksore>
  2. updateDomXMLDemo 的输出结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?xml version="1.0" encoding="UTF-8" >
    <bookstore>
    <book id="1">
    <name>cet4</name>
    <author>college-English</author>
    <price>55</price>
    <language>english</language>
    <country>2</country>
    </book>
    <book id="2">
    <name>cet6</name>
    <author>me</author>
    <price>33</price>
    <language>lalalal</language>
    <country>chinese</country>
    </book>
    </bookstore>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <bookstore>
    <book id="1">
    <name>changedName</name>
    <author>college-English</author>
    <price>55</price>
    <language>english</language>
    <country>2</country>
    </book>
    <book id="2">
    <name>cet6</name>
    <author>me</author>
    <price>33</price>
    <language>lalalal</language>
    <country>chinese</country>
    </book>
    </bookstore>
总结
  1. 上述的结果可以看出在经过修改XML 文件后,数据替换,但是要注意XML 文件的属性问题,例如编码格式的、版本号等变化所引起的问题。
  2. 对于以上的三种关于XML 文件的解析、写入、更新方法,我觉得可以写出更具体的方法供这三种函数调用,具体就是问题细节化,这样可以省去大量的时间开发。
  3. 想理解Dom解析XML 文件的这三种函数,其实也很简单,将要解决的问题进行细分,然后按照不同的步骤进行解决,注重理解不同步骤之间的关系和顺序即可。

Sax 解析XML

简介

Sax 解析是从头到尾逐行逐个元素读取内容,修改较为不便,但适用于只读的大文档。
Sax 采用事件驱动的方式解析文档。

优点

在Sax 的解析过程中,读取到文档开头、结尾,元素的开头和元素的结尾都会触发一些回调方法,你可以在这些回调方法中进行相应事件处理这四个方法是:

  • startDocument()
  • endDocument()
  • startElement()
  • endElement()

此外,只读取到节点处是不够的,我们还需要characters() 方法来仔细处理元素内包含的内容将这些回调方法集合起来,便形成了一个类,这个类也就是我们需要的触发器SaxHandler 。

代码

Sax 代码如下:

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
package com.vgbh;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SaxXMLDemo {

public static void main(String[] args) {
SaxXMLDemo sxd = new SaxXMLDemo();

String uri = "bookstore.xml";
try {
sxd.querySaxXMLDemo (uri);
} catch (Exception e) {
e.printStackTrace();
}
}

//读取XML文件
private void querySaxXMLDemo(String uri) throws Exception {
/*
* 该方法主要依赖于SaxHandler类的方法重写
*/
//实例化SAXparseFactory对象
SAXParserFactory spFactory = SAXParserFactory.newInstance();
//创建解析器
SAXParser sParser = spFactory.newSAXParser();
//获取需要解析的文档,生成解析器,最后解析文档
SaxHandler sh = new SaxHandler();
sParser.parse(uri, sh);
}

}

class SaxHandler extends DefaultHandler {

//character代表传回来的字符数组,内容包含元素。start数组开始的位置。end数组结束的位置。
public void characters (char[] character, int start, int end) throws SAXException {
String content = new String(character, start, end);
System.out.println(content);
super.characters(character, start, end);
}

public void startDocument () throws SAXException {
System.out.println("开始解析文档...");
super.startDocument();
}

public void endDoucement () throws SAXException{
System.out.println("结束解析文档...");
super.endDocument();
}

//uri是名称空间URI localNmae是本地名称 qName是限定名称 attribute很明显是属性的集合 以上的四种变量若不可用,则为空。
public void startElement (String uri, String localName, String qName, Attributes attribute) throws SAXException {
//System.out.print("开始解析元素...");
if (attribute != null) {
for (int i = 0; i < attribute.getLength(); i ++) {
//getQName()是获取属性名称
System.out.print(attribute.getQName(i) + "=\"" + attribute.getValue(i) + "\"");
}
}
System.out.print(qName + ":");
super.startElement(uri, localName, qName, attribute);
}

//uri是名称空间URI arg1是本地名称 qName是限定名称 以上的三种变量若不可用,则为空
public void endElement (String uri, String localNmae, String qName) throws SAXException {
//System.out.print(qName);
//System.out.print("结束解析元素...");
super.endElement(uri, localNmae, qName);
}

}
总结
  1. SAX 解析XML 文件主要依靠的是对SaxHandler 类的方法重写,所以理解原生类的函数很重要。
  2. SaxHandler类在线API:http://docs.basex.org/javadoc/org/basex/build/xml/SAXHandler.html

Dom4j 解析XML

简介

Dom4j 是一种用于解析、写入XML 文件的Java API ,同时由于是开源的,所以可以在GitHub上找到源码。
对于XML的解析,Dom4j 很强大也很好用,并且他的性能、功能和易用性都是非常的好,所以你可以尝试一下使用它。

代码

解析代码如下:

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.vgbh;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;


public class Dom4jXMLDemo {

public static void main(String[] args) {
Dom4jXMLDemo dxd = new Dom4jXMLDemo();

String uri = "bookstore.xml";
String uri1 = "F:/WorkSpace/dom4jDemoC.xml";
dxd.queryDom4jXMLDemo(uri);

//dxd.insertDom4jXMLDemo(uri1);
}

//读取XML文件
public void queryDom4jXMLDemo(String uri) {
try {
//构建SAXReader对象,并读取XML文件并转换为doc
SAXReader sReader = new SAXReader();
Document doc = sReader.read(uri);

//获取根节点
Element root = doc.getRootElement();
//获取元素
List<Element> books = root.elements();

for (int i = 0; i < books.size(); i++) {
//创建一个book对象保存数据
Book book = new Book();
//获取节点的属性值赋给book
Element element = books.get(i);

book.setId(element.attributeValue("id"));
book.setName(element.elementText("name"));
book.setAuthor(element.elementText("author"));
book.setPrice(element.elementText("price"));
book.setLanguage(element.elementText("language"));
book.setCountry(element.elementText("country"));

System.out.println("id:" + book.getId() + " name:" + book.getName() + " author:" + book.getAuthor() + " price:" + book.getPrice() +
" language:" + book.getLanguage() + " country:" + book.getCountry());
}

} catch (DocumentException e) {
e.printStackTrace();
}

}

//写入XML文件
public void insertDom4jXMLDemo (String uri) {
Document doc = DocumentHelper.createDocument();
//增加根节点
Element bookstore = doc.addElement("bookstore");
//增加子元素
Element book1 = bookstore.addElement("book");
//添加子元素的数据名称
Element name1 = book1.addElement("name");
Element author1 = book1.addElement("author");
Element price1 = book1.addElement("price");
Element language1 = book1.addElement("language");
Element country1 = book1.addElement("country");

book1.addAttribute("id", "1");
name1.setText("一生有你");
author1.setText("午歌");
price1.setText("25");
language1.setText("chinese");
country1.setText("china");

//将Doc转换为XML文件
OutputFormat format = OutputFormat.createPrettyPrint();
format.setIndent(" ");//设置缩进格式
format.setEncoding("UTF-8");//设置XML文件的编码格式
XMLWriter writer = null;
try {
writer = new XMLWriter(new FileOutputStream(uri),format);
writer.write(doc);
} catch (IOException e) {
e.printStackTrace();
}
}

}

class Book {
private String id ;
private String name ;
private String author ;
private String price ;
private String language ;
private String country ;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}

结果

  1. queryDom4jXMLDemo 的结果:

    1
    2
    id:1    name:changedName   author:college-English   price:55   language:english   country:2
    id:2 name:cet6 author:me price:33 language:lalalal country:chinese

    从结果中可以看出,在解析XML 文件时,他是将元素返回为对象,再通过获取对象的值从而获得数据。
    我在代码中使用了内部类的方式接受传回来的数据,这样保存数据会方便很多,不过需要即用即销。

  2. insertDom4jXMLDemo的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?xml version="1.0" encoding="UTF-8"?>
    <bookstore>
    <book id="1">
    <name>一生有你</name>
    <author>午歌</author>
    <price>25</price>
    <language>chinese</language>
    <country>china</country>
    </book>
    </bookstore>

    结果显示的很完美,doc树正常显示,格式也没有问题。


Jdom 解析XML

简介

Jdom 只是一种适合Java程序员来使用的Java XML API。
要用Jdom进行开发,需要到jar包,因为是开源的,所以GitHub上也有,

代码

解析代码如下:

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package com.vgbh;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

public class JdomXMLDemo {

public static void main(String[] args) {
JdomXMLDemo jxd = new JdomXMLDemo();

String uri = "bookstore.xml";
String uri1 = "F:/WorkSpace/JdomDemoD.xml";

//jxd.insertJdomXMLDemo(uri1);
//jxd.queryJdomXMLDemo(uri);
jxd.updateJdomXMLDemo(uri,"0");
}

//写入XML文件
public void insertJdomXMLDemo (String uri) {
//添加根节点
Element bookstore = new Element("bookstore");
//添加元素
Element book = new Element("book");
//添加元素的本地名称
Element name = new Element("name");
Element author = new Element("author");
Element price = new Element("price");
Element language = new Element("language");
Element country = new Element("country");

//Document对象
Document doc = new Document(bookstore);

//元素的数据填充
name.setText("names");
author.setText("authors");
price.setText("prices");
language.setText("languages");
country.setText("countrys");

//数据添加入元素
book.addContent(name);
book.addContent(author);
book.addContent(price);
book.addContent(language);
book.addContent(country);
//将元素添加进bookstore
bookstore.addContent(book);

//将doc转换为XML文件
Format format = Format.getCompactFormat();
format.setEncoding("UTF-8");//设置编码格式
format.setIndent(" ");//设置首行缩进

//定义输出对象
XMLOutputter XMLOut = new XMLOutputter();
try {
//将doc转换为XML
XMLOut.output(doc, new FileOutputStream(uri));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("写入成功!");
}

//读取XML文件
public void queryJdomXMLDemo (String uri) {
try {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(uri);

Element book = doc.getRootElement();
List list = book.getChildren("book");

for (int i = 0; i < list.size(); i++) {
Element e = (Element) list.get(i);
// e.getChildText("name");
// e.getChildText("author");
// e.getChildText("price");
// e.getChildText("language");
// e.getChildText("country");

// Element name = e.getChild("name");
// name.setText("xiugaiguode ");

System.out.println("name:" + e.getChildText("name") + " author:" + e.getChildText("author") + " price:" +
e.getChildText("price") + " lanuage:" + e.getChildText("language") + " country:" + e.getChildText("country"));
}
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

//修改XML文件
public void updateJdomXMLDemo (String uri, String id) {
try {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(uri);

Element book = doc.getRootElement();
List list = book.getChildren("book");

for (int i = 0; i < list.size(); i++) {

if (String.valueOf(i).equals(id)) {
Element e = (Element) list.get(i);
// e.getChildText("name");
// e.getChildText("author");
// e.getChildText("price");
// e.getChildText("language");
// e.getChildText("country");
Element name = e.getChild("name");
name.setText("xiugaiguode ");

System.out.println("name:" + e.getChildText("name") + " author:" + e.getChildText("author") + " price:" +
e.getChildText("price") + " lanuage:" + e.getChildText("language") + " country:" + e.getChildText("country"));
}
}

//将doc转换为XML文件
Format format = Format.getCompactFormat();
format.setEncoding("UTF-8");//设置编码格式
format.setIndent(" ");//设置首行缩进

//定义输出对象
XMLOutputter XMLOut = new XMLOutputter();
//将doc转换为XML
XMLOut.output(doc, new FileOutputStream(uri));
System.out.println("修改成功!");
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Digester

使用步骤

  1. 创建一个org.apache.commons.digester3.Digester 类的实例对象。(补充说明一下,只要我们完成了XML 解析操作,并且不是在多个线程中使用同一个Digester 对象,那么就可以安全的重复使用我们创建的这个Digester ,不过从用Digester 并不是很推荐,最好每一个XML 解析对应一个单独的Digester 实例)
  2. 为Digester 配置属性值,通过配置属性值,我们可以改变Digester 的解析行为。(具体配置哪些属性、如何配置)
  3. 可以将我们创建的初始对象push 到Digester 栈里。
  4. 在输入的XML 文档中,给我们所有需要出发的规则(rule )处理需要的元素匹配模式(pattern )注册规则。针对任何一个模式,可以注册任意数量的规则。(如果一个模式对应多个规则,则begin 和body 事件方法会按照他们注册的顺序依次执行,而end 事件方法是倒序执行的)
  5. 最后,调用Digester.parse() 方法,该方法需要传入XML 文件的应用作为参数,该参数支持多种格式的文件流。(该方法会抛出IOException or SAXException 异常,以及各种在解析规则处理时遇到的异常,如NoSuchMethodException、 IllegalAccessException)

简单示例

  1. 将要解析的XML 文件

    1
    2
    3
    4
    5
    <foo name="The Parent">
    <bar id="123" title="The First Child" />
    <bar id="456" title="The Second Child" />
    <bar id="789" title="The Second Child" />
    </foo>
  2. 创建Java Bean 对应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
    package cn.vgbhfive.digesterTest.pojo;

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;

    public class Foo {
    private String name;
    private List<Bar> barList = new ArrayList<Bar>();

    public void addBar(Bar bar) {
    barList.add(bar);
    }

    public Bar findBar(int id) {
    for (Bar bar : barList) {
    if (bar.getId() == id) {
    return bar;
    }
    }
    return null;
    }

    public Iterator<Bar> getBars() {
    return barList.iterator();
    }

    /**
    * @return the name
    */
    public String getName() {
    return name;
    }

    /**
    * @param name the name to set
    */
    public void setName(String name) {
    this.name = name;
    }

    /**
    * @return the barList
    */
    public List<Bar> getBarList() {
    return barList;
    }

    /**
    * @param barList the barList to set
    */
    public void setBarList(List<Bar> barList) {
    this.barList = barList;
    }

    }

    类:

    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
    package cn.vgbhfive.digesterTest.pojo;

    public class Bar {

    private int id;
    private String title;

    /**
    * @return the id
    */
    public int getId() {
    return id;
    }

    /**
    * @param id the id to set
    */
    public void setId(int id) {
    this.id = id;
    }

    /**
    * @return the title
    */
    public String getTitle() {
    return title;
    }

    /**
    * @param title the title to set
    */
    public void setTitle(String title) {
    this.title = title;
    }
    }
  3. 使用Digester 解析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
    package cn.vgbhfive.digesterTest;

    import java.io.IOException;

    import org.apache.commons.digester3.Digester;
    import org.xml.sax.SAXException;

    import apache.commons.digester3.example.pojo.Bar;
    import apache.commons.digester3.example.pojo.Foo;

    public class Main {

    public static void main(String[] args) {

    try {
    //1、创建Digester对象实例
    Digester digester = new Digester();

    //2、配置属性值
    digester.setValidating(false);

    //3、push对象到对象栈
    //digester.push(new Foo());

    //4、设置匹配模式、规则
    digester.addObjectCreate("foo", "cn.vgbhfive.digesterTest.pojo.Foo");
    digester.addSetProperties("foo");
    digester.addObjectCreate("foo/bar", "cn.vgbhfive.digesterTest.pojo.Bar");
    digester.addSetProperties("foo/bar");
    digester.addSetNext("foo/bar", "addBar", "cn.vgbhfive.digesterTest.pojo.Bar");

    //5、开始解析
    Foo foo = digester.parse(Main.class.getClassLoader().getResourceAsStream("example.xml"));

    //6、打印解析结果
    System.out.println(foo.getName());
    for (Bar bar : foo.getBarList()) {
    System.out.println(bar.getId() + "," + bar.getTitle());
    }
    }
    catch (IOException e) {
    e.printStackTrace();
    }
    catch (SAXException e) {
    e.printStackTrace();
    }
    }
    }
  4. 输出结果

    1
    2
    3
    4
    The Parent
    123,The First Child
    456,The Second Child
    789,The Second Child

    上述代码涉及类型的自动转换,所有的类型转换都是由commons-beanutils 包中的ConverUtils 来完成。

Digester 属性配置

org.apache.commons.digester3.Digester 实例对象包含若干成员属性,这些属性值是可以设置的,以便自定义解析操作。
可以配置的属性:

  • classLoader:
  • errorHandler:
  • namespaceAware:
  • xincludeAware:
  • ruleNamespaceURL:
  • rules:
  • useContextClassLoader:
  • validating:

为了让配置生效,属性值的更改一定要在parse方法调用之前设置。
当Digester 遇到DOCTYPE 声明时,可以使用本地dtd 文件。

Digester 对象栈

Digester 使用的一个核心技术就是动态构建一颗Java 对象树,在构建的过程中,最重要的辅助数据结构即对象栈

以简单示例的XML 为例:
首先会创建一个Foo 对象,并压入对象栈,然后设置Foo 的属性值name,紧接着创建Bar 对象并压入栈,然后设置Bar 的属性值,然后将该Bar 对象添加的到Foo 对象的barlist 属性集合中,然后Bar 对象弹出对象栈。
以此类推,遇到起始标记的元素创建对象入栈,遇到结尾标记的元素做出栈操作,出栈前,需要将出栈对象并关联到上一个栈顶对象。
最终,解析完XML 后,留在栈顶的就关联了所有在xml解析中创建的动态对象。

Digester暴露出的与对象栈操作API如下所示:

  • clear(): 清除对象栈。
  • peek(): 返回栈顶对象引用,但是不弹出。
  • pop(): 返回栈顶对象,并弹出。
  • push(): 入栈操作。

Digester 元素匹配模式

Digester 的一个关键特性是可以自动识别xml的层次结构,程序员只需要关心遇到匹配到某个元素后需要做哪些操作即可。

1
2
3
4
5
6
7
8
9
10
11
<a>         	-- Matches pattern "a"
<b> -- Matches pattern "a/b"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
</b>
<b> -- Matches pattern "a/b"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
<c/> -- Matches pattern "a/b/c"
</b>
</a>

Digester 规则处理

当匹配到模式时,会触发规则处理,具体的规则处理机制是由org.apache.commons.digester3.Rule 接口封装的实现类处理。
Rule 接口定义的方法:

  • begin(): 匹配到xml元素开始标记时,调用该方法。
  • body(): 匹配到xml元素body时,调用该方法。
  • end(): 匹配到xml元素结束标记时,调用该方法。
  • finish(): 当所有解析方法解析完毕后,调用该方法,用于清楚临时数据等。

Digester 提供的实现Rule 接口的实现类:

  • ObjectCreateRule
  • FactoryCreateRule
  • SetPropertiesRule
  • SetPropertyRule
  • SetNextRule
  • SetTopRule
  • CallMethodRule
  • CallParamRule
  • NodeCreateRule

详细内容请见官方API 文档。

Digester 日志

日志是调试、排查错误非常关键的一个环节,Digester 记录了非常详细的日志,我们可以按如下方式来开启日志打印功能。

  1. pom.xml 添加依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
  2. log4j 配置文件log4j.properties

    1
    2
    3
    4
    5
    6
    7
    8
    ### set log levels ###
    log4j.rootLogger = debug, stdout

    ### \u8F93\u51FA\u5230\u63A7\u5236\u53F0 ###
    log4j.appender.stdout = org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target = System.out
    log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
  3. DEBUG 日志

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    2017-06-04 18:26:33  [ main:51 ] - [ DEBUG ]    Fire body() for SetPropertiesRule[aliases={}, ignoreMissingProperty=true]
    2017-06-04 18:26:33 [ main:51 ] - [ DEBUG ] Popping body text ''
    2017-06-04 18:26:33 [ main:51 ] - [ DEBUG ] Fire end() for SetPropertiesRule[aliases={}, ignoreMissingProperty=true]
    2017-06-04 18:26:33 [ main:52 ] - [ DEBUG ] Fire end() for ObjectCreateRule[className=apache.commons.digester3.example.pojo.Foo, attributeName=null]
    2017-06-04 18:26:33 [ main:52 ] - [ DEBUG ] [ObjectCreateRule]{foo} Pop 'apache.commons.digester3.example.pojo.Foo'
    2017-06-04 18:26:33 [ main:52 ] - [ DEBUG ] endDocument()
    The Parent
    123,The First Child
    456,The Second Child
    789,The Second Child

实例开发

  1. XML 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <web-app>
    <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
    <param-name>application</param-name>
    <param-value>org.apache.struts.example.ApplicationResources</param-value>
    </init-param>
    <init-param>
    <param-name>config</param-name>
    <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    </servlet>
    </web-app>
  2. Java Bean 文件

    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
    import java.util.HashMap;
    import java.util.Map;

    public class ServletBean {

    private String servletName;
    private String servletClass;
    private Map<String, String> initParams = new HashMap<String, String>();

    public void addInitParam(String paramName, String paramValue) {
    initParams.put(paramName, paramValue);
    }
    /**
    * @return the servletName
    */
    public String getServletName() {
    return servletName;
    }
    /**
    * @param servletName the servletName to set
    */
    public void setServletName(String servletName) {
    this.servletName = servletName;
    }
    /**
    * @return the servletClass
    */
    public String getServletClass() {
    return servletClass;
    }
    /**
    * @param servletClass the servletClass to set
    */
    public void setServletClass(String servletClass) {
    this.servletClass = servletClass;
    }
    /**
    * @return the initParams
    */
    public Map<String, String> getInitParams() {
    return initParams;
    }
    /**
    * @param initParams the initParams to set
    */
    public void setInitParams(Map<String, String> initParams) {
    this.initParams = initParams;
    }
    }
  3. 编写规则解析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
    import java.io.IOException;

    import org.apache.commons.digester3.Digester;
    import org.apache.commons.digester3.Rule;
    import org.apache.commons.digester3.SetNextRule;
    import org.xml.sax.SAXException;

    import apache.commons.digester3.example.pojo.Bar;
    import apache.commons.digester3.example.pojo.Foo;
    import apache.commons.digester3.example.pojo.ServletBean;

    public class WebMain {

    public static void main(String[] args) {
    try {
    // 1、创建Digester对象实例
    Digester digester = new Digester();

    // 2、配置属性值
    digester.setValidating(false);

    // 3、push对象到对象栈

    // 4、设置匹配模式、规则
    digester.addObjectCreate("web-app/servlet", "apache.commons.digester3.example.pojo.ServletBean");
    digester.addCallMethod("web-app/servlet/servlet-name", "setServletName", 0);
    digester.addCallMethod("web-app/servlet/servlet-class", "setServletClass", 0);
    digester.addCallMethod("web-app/servlet/init-param", "addInitParam", 2);
    digester.addCallParam("web-app/servlet/init-param/param-name", 0);
    digester.addCallParam("web-app/servlet/init-param/param-value", 1);

    // 5、开始解析
    ServletBean servletBean = digester
    .parse(ExampleMain.class.getClassLoader().getResourceAsStream("web.xml"));

    // 6、打印解析结果
    System.out.println(servletBean.getServletName());
    System.out.println(servletBean.getServletClass());
    for(String key : servletBean.getInitParams().keySet()){
    System.out.println(key + ": " + servletBean.getInitParams().get(key));
    }

    }
    catch (IOException e) {
    e.printStackTrace();
    }
    catch (SAXException e) {
    e.printStackTrace();
    }
    }
    }
  4. 输出结果

    1
    2
    3
    4
    action
    org.apache.struts.action.ActionServlet
    application: org.apache.struts.example.ApplicationResources
    config: /WEB-INF/struts-config.xml

错误排查

Digester 是基于SAX 开发的. Digestion 会抛出两种类型的Exception:

  • java.io.IOException
  • org.xml.sax.SAXException

一般情况下SAXException 异常内部会组合另一个异常,换句话说,就是当Digester 遇到异常的时候,会首先将该异常封装成一个SAXException 异常,然后将该SAXException 重新抛出。所以在捕获SAXException 异常,并仔细检查被封装的内部异常,有助于排查错误;


参考资料

https://blog.csdn.net/qq_36594739 (这是我以前的博客)
http://commons.apache.org/proper/commons-digester/guide/core.html
https://www.cnblogs.com/chenpi/p/6930730.html


个人备注

此博客内容均为作者学习所做笔记,侵删!
若转作其他用途,请注明来源!