Java中使用DOM和SAX解析XML文件的方法示例
dom4j介紹
dom4j的項(xiàng)目地址:http://sourceforge.net/projects/dom4j/?source=directory
dom4j是一個(gè)簡(jiǎn)單的開(kāi)源庫(kù),用于處理XML、 XPath和XSLT,它基于Java平臺(tái),使用Java的集合框架,全面集成了DOM,SAX和JAXP。
dom4j的使用
下載了dom4j項(xiàng)目之后,解壓縮,將其jar包(我的當(dāng)前版本叫做dom4j-1.6.1.jar)加入class path下面。
?。≒roperties->Java Build Path -> Add External JARs...)。
之后就可以使用其提供的API進(jìn)行編程。
程序?qū)嵗?
第一個(gè)程序,用Java代碼生成xml文檔,代碼如下:
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框架學(xué)習(xí) 使用dom4j框架創(chuàng)建xml文檔并輸出保存
*
*/
public class Dom4JTest1
{
public static void main(String[] args) throws Exception
{
// 第一種方式:創(chuàng)建文檔,并創(chuàng)建根元素
// 創(chuàng)建文檔:使用了一個(gè)Helper類(lèi)
Document document = DocumentHelper.createDocument();
// 創(chuàng)建根節(jié)點(diǎn)并添加進(jìn)文檔
Element root = DocumentHelper.createElement("student");
document.setRootElement(root);
// 第二種方式:創(chuàng)建文檔并設(shè)置文檔的根元素節(jié)點(diǎn)
Element root2 = DocumentHelper.createElement("student");
Document document2 = DocumentHelper.createDocument(root2);
// 添加屬性
root2.addAttribute("name", "zhangsan");
// 添加子節(jié)點(diǎn):add之后就返回這個(gè)元素
Element helloElement = root2.addElement("hello");
Element worldElement = root2.addElement("world");
helloElement.setText("hello Text");
worldElement.setText("world text");
// 輸出
// 輸出到控制臺(tái)
XMLWriter xmlWriter = new XMLWriter();
xmlWriter.write(document);
// 輸出到文件
// 格式
OutputFormat format = new OutputFormat(" ", true);// 設(shè)置縮進(jìn)為4個(gè)空格,并且另起一行為true
XMLWriter xmlWriter2 = new XMLWriter(
new FileOutputStream("student.xml"), format);
xmlWriter2.write(document2);
// 另一種輸出方式,記得要調(diào)用flush()方法,否則輸出的文件中顯示空白
XMLWriter xmlWriter3 = new XMLWriter(new FileWriter("student2.xml"),
format);
xmlWriter3.write(document2);
xmlWriter3.flush();
// close()方法也可以
}
}
程序Console輸出:
<?xml version="1.0" encoding="UTF-8"?> <student/>
生成的一個(gè)xml文檔:
<?xml version="1.0" encoding="UTF-8"?> <student name="zhangsan"> <hello>hello Text</hello> <world>world text</world> </student>
程序?qū)嵗?
程序?qū)嵗?,讀入xml文檔并分析,將其內(nèi)容輸出。
首先,待分析的文檔如下:
<?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框架學(xué)習(xí): 讀取并解析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());
// 獲取特定名稱(chēng)的子元素
List<Element> childList2 = root.elements("hello");
System.out.println("hello child: " + childList2.size());
// 獲取名字為指定名稱(chēng)的第一個(gè)子元素
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();
// 注意要用完整類(lèi)名
org.w3c.dom.Document document2 = db.parse(new File("students.xml "));
DOMReader domReader = new DOMReader();
// 將JAXP的Document轉(zhuǎn)換為dom4j的Document
Document document3 = domReader.read(document2);
Element rootElement = document3.getRootElement();
System.out.println("Root: " + rootElement.getName());
}
}
代碼運(yùn)行后輸出:
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實(shí)現(xiàn)實(shí)體解析的步驟
//下面使用XMLReader 來(lái)解析
(一)第一步:新建一個(gè)工廠類(lèi)SAXParserFactory,代碼如下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:讓工廠類(lèi)產(chǎn)生一個(gè)SAX的解析類(lèi)SAXParser,代碼如下:
SAXParser parser = factory.newSAXParser();
(三)第三步:從SAXPsrser中得到一個(gè)XMLReader實(shí)例,代碼如下:
XMLReader reader = parser.getXMLReader();
(四)第四步:把自己寫(xiě)的handler注冊(cè)到XMLReader中,一般最重要的就是ContentHandler,代碼如下:
reader.setContentHandler(this);
(五)第五步:將一個(gè)xml文檔或者資源變成一個(gè)java可以處理的InputStream流后,解析正式開(kāi)始,代碼如下:
reader.parse(new InputSource(is));
//下面使用SAXParser來(lái)解析
(一)第一步:新建一個(gè)工廠類(lèi)SAXParserFactory,代碼如下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:讓工廠類(lèi)產(chǎn)生一個(gè)SAX的解析類(lèi)SAXParser,代碼如下:
SAXParser parser = factory.newSAXParser();
(三)第三步:將一個(gè)xml文檔或者資源變成一個(gè)java可以處理的InputStream流后,解析正式開(kāi)始,代碼如下:
parser.parse(is,this);
估計(jì)大家都看到了ContentHandler ,下面具體的講下
解析開(kāi)始之前,需要向XMLReader/SAXParser 注冊(cè)一個(gè)ContentHandler,也就是相當(dāng)于一個(gè)事件監(jiān)聽(tīng)器,在ContentHandler中定義了很多方法
//設(shè)置一個(gè)可以定位文檔內(nèi)容事件發(fā)生位置的定位器對(duì)象
public void setDocumentLocator(Locator locator)
//用于處理文檔解析開(kāi)始事件
public void startDocument()throws SAXException
//處理元素開(kāi)始事件,從參數(shù)中可以獲得元素所在名稱(chēng)空間的uri,元素名稱(chēng),屬性類(lèi)表等信息
public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException
//處理元素結(jié)束事件,從參數(shù)中可以獲得元素所在名稱(chēng)空間的uri,元素名稱(chēng)等信息
public void endElement(String namespacesURI , String localName , String qName) throws SAXException
//處理元素的字符內(nèi)容,從參數(shù)中可以獲得內(nèi)容
public void characters(char[] ch , int start , int length) throws SAXException
順便介紹下XMLReader中的方法。
//注冊(cè)處理XML文檔解析事件ContentHandler
public void setContentHandler(ContentHandler handler)
//開(kāi)始解析一個(gè)XML文檔
public void parse(InputSorce input) throws SAXException
大概的講的差不多了 接下來(lái)開(kāi)始講解解析的步驟
我們還是用上一章的代碼
首先 我們創(chuàng)建一個(gè)Person類(lèi) 用來(lái)存儲(chǔ)用戶(hù)的信息
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;
}
}
接下來(lái) 我們要實(shí)現(xiàn)一個(gè)ContentHandler 用來(lái)解析XML
實(shí)現(xiàn)一個(gè)ContentHandler 一般需要下面幾個(gè)步驟
1、聲明一個(gè)類(lèi),繼承DefaultHandler。DefaultHandler是一個(gè)基類(lèi),這個(gè)類(lèi)里面簡(jiǎn)單實(shí)現(xiàn)了一個(gè)ContentHandler。我們只需要重寫(xiě)里面的方法即可。
2、重寫(xiě) startDocument() 和 endDocument(),一般將正式解析之前的初始化放到startDocument()里面,收尾的工作放到endDocument()里面。
3、重寫(xiě)startElement(),XML解析器遇到XML里面的tag時(shí)就會(huì)調(diào)用這個(gè)函數(shù)。經(jīng)常在這個(gè)函數(shù)內(nèi)是通過(guò)對(duì)localName的值進(jìn)行判斷而操作一些數(shù)據(jù)。
4、重寫(xiě)characters()方法,這是一個(gè)回調(diào)方法。解析器執(zhí)行完startElement()后,解析節(jié)點(diǎn)的內(nèi)容后就會(huì)執(zhí)行這個(gè)方法,并且參數(shù)ch[]就是節(jié)點(diǎn)的內(nèi)容。
5、重寫(xiě)endElement()方法,這個(gè)方法與startElement()相對(duì)應(yīng),解析完一個(gè)tag節(jié)點(diǎn)后,執(zhí)行這個(gè)方法,解析一個(gè)tag后,調(diào)用這個(gè)處理還原和清除相關(guān)信息
首先 新建一個(gè)類(lèi) 繼承DefaultHandler 并重寫(xiě)以下幾個(gè)方法
public class SAX_parserXML extends DefaultHandler {
/**
* 當(dāng)開(kāi)始解析xml文件的聲明的時(shí)候就會(huì)觸發(fā)這個(gè)事件, 可以做一些初始化的工作
* */
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super.startDocument();
}
/**
* 當(dāng)開(kāi)始解析元素的開(kāi)始標(biāo)簽的時(shí)候,就會(huì)觸發(fā)這個(gè)事件
* */
@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);
}
/**
* 當(dāng)讀到文本元素的時(shí)候要觸發(fā)這個(gè)事件.
* */
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
super.characters(ch, start, length);
}
/**
* 當(dāng)讀到結(jié)束標(biāo)簽的時(shí)候 就會(huì)觸發(fā)這個(gè)事件
* */
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
super.endElement(uri, localName, qName);
}
}
首先 我們創(chuàng)建一個(gè)list 用來(lái)保存解析出來(lái)的person數(shù)據(jù)
List<Person> persons;
但是?在哪里初始化呢?我們可以在startDocument()里面初始化,因?yàn)楫?dāng)開(kāi)始解析xml文件的聲明的時(shí)候就會(huì)觸發(fā)這個(gè)事件所以放在這里比較合適
/**
* 當(dāng)開(kāi)始解析xml文件的聲明的時(shí)候就會(huì)觸發(fā)這個(gè)事件, 可以做一些初始化的工作
* */
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super.startDocument();
// 初始化list
persons = new ArrayList<Person>();
}
接下來(lái) 就要開(kāi)始解析了
/**
* 當(dāng)開(kāi)始解析元素的開(kāi)始標(biāo)簽的時(shí)候,就會(huì)觸發(fā)這個(gè)事件
* */
@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標(biāo)簽 開(kāi)始存儲(chǔ)
if (localName.equals("person")) {
person = new Person();
person.set_id(attributes.getValue("id"));
}
curNode = localName;
}
上面的代碼中 localName表示當(dāng)前解析到的元素名
//步驟
//1.判斷是否是person元素
//2.創(chuàng)建新的Person對(duì)象
//3.獲取id 添加到Person對(duì)象中
curNode 用來(lái)保存當(dāng)前的元素名 在characters中會(huì)使用到
/**
* 當(dāng)讀到文本元素的時(shí)候要觸發(fā)這個(gè)事件.
* */
@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) {
//取出目前元素對(duì)應(yīng)的值
String txt = new String(ch, start, length);
//判斷元素是否是name
if (curNode.equals("name")) {
//將取出的值添加到person對(duì)象
person.set_name(txt);
} else if (curNode.equals("age")) {
person.set_age(txt);
}
}
}
接下來(lái)是介紹標(biāo)簽結(jié)束的時(shí)候需要做的事情
/**
* 當(dāng)讀到結(jié)束標(biāo)簽的時(shí)候 就會(huì)觸發(fā)這個(gè)事件
* */
@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 = "";
}
解析的事情結(jié)束了 大概流程就是
1.一個(gè)元素開(kāi)始時(shí) 會(huì)調(diào)用startElement方法
2.接下來(lái)會(huì)調(diào)用到characters方法,可以用來(lái)獲取元素的值
3.一個(gè)元素結(jié)束時(shí) 會(huì)調(diào)用到endElement方法
解析結(jié)束之后 我們需要寫(xiě)一個(gè)方法 用來(lái)獲取解析后保存的list
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對(duì)象傳入 就可以解析出內(nèi)容
看完了代碼,我來(lái)給出完整的代碼
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;
// 當(dāng)前節(jié)點(diǎn)
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;
}
/**
* 當(dāng)開(kāi)始解析xml文件的聲明的時(shí)候就會(huì)觸發(fā)這個(gè)事件, 可以做一些初始化的工作
* */
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
super.startDocument();
// 初始化list
persons = new ArrayList<Person>();
}
/**
* 當(dāng)開(kāi)始解析元素的開(kāi)始標(biāo)簽的時(shí)候,就會(huì)觸發(fā)這個(gè)事件
* */
@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標(biāo)簽 開(kāi)始存儲(chǔ)
if (localName.equals("person")) {
person = new Person();
person.set_id(attributes.getValue("id"));
}
curNode = localName;
}
/**
* 當(dāng)讀到文本元素的時(shí)候要觸發(fā)這個(gè)事件.
* */
@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) {
// 取出目前元素對(duì)應(yīng)的值
String txt = new String(ch, start, length);
// 判斷元素是否是name
if (curNode.equals("name")) {
// 將取出的值添加到person對(duì)象
person.set_name(txt);
} else if (curNode.equals("age")) {
person.set_age(txt);
}
}
}
/**
* 當(dāng)讀到結(jié)束標(biāo)簽的時(shí)候 就會(huì)觸發(fā)這個(gè)事件
* */
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
super.endElement(uri, localName, qName);
// 如果是person結(jié)尾 并且person不為空,添加到list
if (localName.equals("person") && person != null) {
persons.add(person);
person = null;
}
curNode = "";
}
}
寫(xiě)個(gè)方法調(diào)用下這個(gè)類(lèi)
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();
如果你看到下面的界面 說(shuō)明解析成功了~

小結(jié):
DOM(文件對(duì)象模型)解析:解析器讀入整個(gè)文檔,然后構(gòu)建一個(gè)駐留內(nèi)存的樹(shù)結(jié)構(gòu),然后代碼就可以根據(jù)DOM接口來(lái)操作這個(gè)樹(shù)結(jié)構(gòu)了。
優(yōu)點(diǎn):整個(gè)文檔讀入內(nèi)存,方便操作:支持修改、刪除和重現(xiàn)排列等多種功能。
缺點(diǎn):將整個(gè)文檔讀入內(nèi)存中,保留了過(guò)多的不需要的節(jié)點(diǎn),浪費(fèi)內(nèi)存和空間。
使用場(chǎng)合:一旦讀入文檔,還需要多次對(duì)文檔進(jìn)行操作,并且在硬件資源充足的情況下(內(nèi)存,CPU)。
為了解決DOM解析存在的問(wèn)題,就出現(xiàn)了SAX解析。其特點(diǎn)為:
優(yōu)點(diǎn):不用實(shí)現(xiàn)調(diào)入整個(gè)文檔,占用資源少。尤其在嵌入式環(huán)境中,如android,極力推薦使用SAX解析。
缺點(diǎn):不像DOM解析一樣將文檔長(zhǎng)期駐留在內(nèi)存中,數(shù)據(jù)不是持久的。如果事件過(guò)后沒(méi)有保存數(shù)據(jù),數(shù)據(jù)就會(huì)丟失。
使用場(chǎng)合:機(jī)器有性能限制
相關(guān)文章
深入淺出重構(gòu)Mybatis與Spring集成的SqlSessionFactoryBean(上)
通常來(lái)講,重構(gòu)是指不改變功能的情況下優(yōu)化代碼,但本文所說(shuō)的重構(gòu)也包括了添加功能。這篇文章主要介紹了重構(gòu)Mybatis與Spring集成的SqlSessionFactoryBean(上)的相關(guān)資料,需要的朋友可以參考下2016-11-11
mybaties plus實(shí)體類(lèi)設(shè)置typeHandler不生效的解決
這篇文章主要介紹了mybaties plus實(shí)體類(lèi)設(shè)置typeHandler不生效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
關(guān)于idea更新到2020.2.3無(wú)法創(chuàng)建web項(xiàng)目原因 library is not specified
這篇文章主要介紹了關(guān)于idea更新到2020.2.3無(wú)法創(chuàng)建web項(xiàng)目原因 library is not specified,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
springboot?加載本地jar到maven的實(shí)現(xiàn)方法
如何在SpringBoot項(xiàng)目中加載本地jar到Maven本地倉(cāng)庫(kù),使用Maven的install-file目標(biāo)來(lái)實(shí)現(xiàn),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-01-01
Java類(lèi)變量和成員變量初始化過(guò)程的應(yīng)用介紹
昨天看了一本叫做《突破程序員基本功的16課》的書(shū),個(gè)人感覺(jué)還可以,主要對(duì)Java的技巧進(jìn)行了一些深入的講解,讓我對(duì)類(lèi)的初始化和對(duì)象的創(chuàng)建有了新的認(rèn)識(shí)2013-04-04
解決SpringBoot多模塊發(fā)布時(shí)99%的問(wèn)題
本文歸納了以下 8 個(gè)原則和發(fā)布時(shí)經(jīng)常出現(xiàn)的 4 個(gè)問(wèn)題的解決方案,掌握了這些原則和解決方案,幾乎可以解決絕大數(shù)SpringBoot發(fā)布問(wèn)題2019-07-07
Java?將PDF轉(zhuǎn)為HTML時(shí)保存到流的方法和步驟
本文介紹如何通過(guò)Java后端程序代碼將PDF文件轉(zhuǎn)為HTML,并將轉(zhuǎn)換后的HTML文件保存到流,下面是實(shí)現(xiàn)轉(zhuǎn)換的方法和步驟,感興趣的朋友一起看看吧2022-01-01

