Java中使用DOM和SAX解析XML文件的方法示例
dom4j介紹
dom4j的項(xiàng)目地址:http://sourceforge.net/projects/dom4j/?source=directory
dom4j是一個(gè)簡單的開源庫,用于處理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類 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()); // 獲取特定名稱的子元素 List<Element> childList2 = root.elements("hello"); System.out.println("hello child: " + childList2.size()); // 獲取名字為指定名稱的第一個(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(); // 注意要用完整類名 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 來解析
(一)第一步:新建一個(gè)工廠類SAXParserFactory,代碼如下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:讓工廠類產(chǎn)生一個(gè)SAX的解析類SAXParser,代碼如下:
SAXParser parser = factory.newSAXParser();
(三)第三步:從SAXPsrser中得到一個(gè)XMLReader實(shí)例,代碼如下:
XMLReader reader = parser.getXMLReader();
(四)第四步:把自己寫的handler注冊到XMLReader中,一般最重要的就是ContentHandler,代碼如下:
reader.setContentHandler(this);
(五)第五步:將一個(gè)xml文檔或者資源變成一個(gè)java可以處理的InputStream流后,解析正式開始,代碼如下:
reader.parse(new InputSource(is));
//下面使用SAXParser來解析
(一)第一步:新建一個(gè)工廠類SAXParserFactory,代碼如下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:讓工廠類產(chǎn)生一個(gè)SAX的解析類SAXParser,代碼如下:
SAXParser parser = factory.newSAXParser();
(三)第三步:將一個(gè)xml文檔或者資源變成一個(gè)java可以處理的InputStream流后,解析正式開始,代碼如下:
parser.parse(is,this);
估計(jì)大家都看到了ContentHandler ,下面具體的講下
解析開始之前,需要向XMLReader/SAXParser 注冊一個(gè)ContentHandler,也就是相當(dāng)于一個(gè)事件監(jiān)聽器,在ContentHandler中定義了很多方法
//設(shè)置一個(gè)可以定位文檔內(nèi)容事件發(fā)生位置的定位器對象
public void setDocumentLocator(Locator locator)
//用于處理文檔解析開始事件
public void startDocument()throws SAXException
//處理元素開始事件,從參數(shù)中可以獲得元素所在名稱空間的uri,元素名稱,屬性類表等信息
public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException
//處理元素結(jié)束事件,從參數(shù)中可以獲得元素所在名稱空間的uri,元素名稱等信息
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中的方法。
//注冊處理XML文檔解析事件ContentHandler
public void setContentHandler(ContentHandler handler)
//開始解析一個(gè)XML文檔
public void parse(InputSorce input) throws SAXException
大概的講的差不多了 接下來開始講解解析的步驟
我們還是用上一章的代碼
首先 我們創(chuàng)建一個(gè)Person類 用來存儲(chǔ)用戶的信息
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; } }
接下來 我們要實(shí)現(xiàn)一個(gè)ContentHandler 用來解析XML
實(shí)現(xiàn)一個(gè)ContentHandler 一般需要下面幾個(gè)步驟
1、聲明一個(gè)類,繼承DefaultHandler。DefaultHandler是一個(gè)基類,這個(gè)類里面簡單實(shí)現(xiàn)了一個(gè)ContentHandler。我們只需要重寫里面的方法即可。
2、重寫 startDocument() 和 endDocument(),一般將正式解析之前的初始化放到startDocument()里面,收尾的工作放到endDocument()里面。
3、重寫startElement(),XML解析器遇到XML里面的tag時(shí)就會(huì)調(diào)用這個(gè)函數(shù)。經(jīng)常在這個(gè)函數(shù)內(nèi)是通過對localName的值進(jìn)行判斷而操作一些數(shù)據(jù)。
4、重寫characters()方法,這是一個(gè)回調(diào)方法。解析器執(zhí)行完startElement()后,解析節(jié)點(diǎn)的內(nèi)容后就會(huì)執(zhí)行這個(gè)方法,并且參數(shù)ch[]就是節(jié)點(diǎn)的內(nèi)容。
5、重寫endElement()方法,這個(gè)方法與startElement()相對應(yīng),解析完一個(gè)tag節(jié)點(diǎn)后,執(zhí)行這個(gè)方法,解析一個(gè)tag后,調(diào)用這個(gè)處理還原和清除相關(guān)信息
首先 新建一個(gè)類 繼承DefaultHandler 并重寫以下幾個(gè)方法
public class SAX_parserXML extends DefaultHandler { /** * 當(dāng)開始解析xml文件的聲明的時(shí)候就會(huì)觸發(fā)這個(gè)事件, 可以做一些初始化的工作 * */ @Override public void startDocument() throws SAXException { // TODO Auto-generated method stub super.startDocument(); } /** * 當(dāng)開始解析元素的開始標(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 用來保存解析出來的person數(shù)據(jù)
List<Person> persons;
但是?在哪里初始化呢?我們可以在startDocument()里面初始化,因?yàn)楫?dāng)開始解析xml文件的聲明的時(shí)候就會(huì)觸發(fā)這個(gè)事件所以放在這里比較合適
/** * 當(dāng)開始解析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)開始解析元素的開始標(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)簽 開始存儲(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對象 //3.獲取id 添加到Person對象中 curNode 用來保存當(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) { //取出目前元素對應(yīng)的值 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); } } }
接下來是介紹標(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è)元素開始時(shí) 會(huì)調(diào)用startElement方法
2.接下來會(huì)調(diào)用到characters方法,可以用來獲取元素的值
3.一個(gè)元素結(jié)束時(shí) 會(huì)調(diào)用到endElement方法
解析結(jié)束之后 我們需要寫一個(gè)方法 用來獲取解析后保存的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對象傳入 就可以解析出內(nè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)開始解析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)開始解析元素的開始標(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)簽 開始存儲(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) { // 取出目前元素對應(yīng)的值 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); } } } /** * 當(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 = ""; } }
寫個(gè)方法調(diào)用下這個(gè)類
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();
如果你看到下面的界面 說明解析成功了~
小結(jié):
DOM(文件對象模型)解析:解析器讀入整個(gè)文檔,然后構(gòu)建一個(gè)駐留內(nèi)存的樹結(jié)構(gòu),然后代碼就可以根據(jù)DOM接口來操作這個(gè)樹結(jié)構(gòu)了。
優(yōu)點(diǎn):整個(gè)文檔讀入內(nèi)存,方便操作:支持修改、刪除和重現(xiàn)排列等多種功能。
缺點(diǎn):將整個(gè)文檔讀入內(nèi)存中,保留了過多的不需要的節(jié)點(diǎn),浪費(fèi)內(nèi)存和空間。
使用場合:一旦讀入文檔,還需要多次對文檔進(jìn)行操作,并且在硬件資源充足的情況下(內(nèi)存,CPU)。
為了解決DOM解析存在的問題,就出現(xiàn)了SAX解析。其特點(diǎn)為:
優(yōu)點(diǎn):不用實(shí)現(xiàn)調(diào)入整個(gè)文檔,占用資源少。尤其在嵌入式環(huán)境中,如android,極力推薦使用SAX解析。
缺點(diǎn):不像DOM解析一樣將文檔長期駐留在內(nèi)存中,數(shù)據(jù)不是持久的。如果事件過后沒有保存數(shù)據(jù),數(shù)據(jù)就會(huì)丟失。
使用場合:機(jī)器有性能限制
相關(guān)文章
Java中內(nèi)存異常StackOverflowError與OutOfMemoryError詳解
這篇文章主要介紹了 Java中內(nèi)存異常StackOverflowError與OutOfMemoryError詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03Java Swing JProgressBar進(jìn)度條的實(shí)現(xiàn)示例
這篇文章主要介紹了Java Swing JProgressBar進(jìn)度條的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12SpringBoot集成tomcat詳解實(shí)現(xiàn)過程
采用spring boot之后,一切變得如此簡單,打包->java-jar->運(yùn)維,只需要一個(gè)jar包便可以隨意部署安裝。這篇文章,將對 spring boot集成tomcat的源碼進(jìn)行分析,探索其內(nèi)部的原理2023-02-02java.sql.SQLTimeoutException異常的正確解決方法(親測有效!)
在我們編寫程序的時(shí)候,有時(shí)候要進(jìn)行復(fù)雜的查詢時(shí),就會(huì)出現(xiàn)執(zhí)行sql時(shí)間過長,引起頁面執(zhí)行不了并提示執(zhí)行腳本超時(shí),這就是我們遇到超時(shí)異常,這篇文章主要給大家介紹了關(guān)于java.sql.SQLTimeoutException異常的正確解決方法,需要的朋友可以參考下2024-02-02javaWEB中前后臺(tái)亂碼問題的解決方法總結(jié)
下面小編就為大家?guī)硪黄猨avaWEB中前后臺(tái)亂碼問題的解決方法總結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08Java旋轉(zhuǎn)數(shù)組中最小數(shù)字具體實(shí)現(xiàn)(圖文詳解版)
這篇文章主要給大家介紹了關(guān)于Java旋轉(zhuǎn)數(shù)組中最小數(shù)字具體實(shí)現(xiàn)的相關(guān)資料,旋轉(zhuǎn)數(shù)組,說明數(shù)據(jù)不變,只是改變位置,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08