很久沒有寫博客了,我認爲黑馬最最可貴的地方在於培養一個開發人員寫博客的習慣。在寫博客的過程當中,可以鞏固以前的舊知識,同時增加了發現新問題的機率,對於提高自己是一個很好的途徑。廢話不多說,就將XML的兩個常用解析器拿來記錄一下。
一、Pull、DOM4J解析器概述
在概述部分,我都將這兩個解析器按照自己的理解來闡述一下。自己是菜鳥,就不要寫出高大上的話,這樣才能真正把知識轉化爲自己的。
1.Pull解析器
Pull解析器是Android內置的XML解析器。Pull的內部,實現的是基於事件驅動的SAX解析思想,一次讀取一行XML文件,並加載一行,解析一行。Pull的有點在於其可以控制SAX讀取指針的動作,程序讓它往下走,指針纔會讀取下一行。
2.DOM4J
DOM4J是開發JDOM的意外產物。據說開發JDOM的一幫子人內訌,一幫說要這樣,一幫說要那樣,然後分家。分家出去的那幫人做了DOM4J,又據說性能等各方面完暴JDOM,成了現在最流行的XML解析器。書寫簡單,效率高,集合了SAX和DOM思想,怎一個“屌”字了得~
二、兩種解析器的應用
1.Pull解析器使用步驟
使用Pull解析XML文件的大致步驟如下:
第一步:
創建一個解析器工廠
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
第二步:
通過解析器工廠,獲取一個解析器對象
XmlPullParser parser = factory.newPullParser();
第三步:
爲解析器設置一個輸入流,關聯需要解析的XML文件
parser.setInput(new FileInputStream("student.xml"), "utf-8");
假設這個需要解析的就是這個student.xml文件,並指定解析使用的字符編碼是utf-8格式。
第四步:
獲取解析器事件類型,可以根據事件類型來執行相對應的操作
int eventType = parser.getEventType();
事件類型是一個整型數據,代表着解析器類當中的諸如START_DOCUMENT的一系列常量。
下面的例子,就是解析一個student.xml文件,並將文件中的每個元素,都封裝成Student對象,存放入List集合中,這個例子的關鍵點在於分清楚每一個事件類型應該採取什麼樣的操作。
先來看看這個XML文件:
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student number="s007">
<name>zs</name>
<age>14</age>
<sex>male</sex>
<score>91</score>
</student>
<student number="s002">
<name>wangwu</name>
<age>24</age>
<sex>male</sex>
<score>87</score>
</student>
<student number="s003">
<name>lisi</name>
<age>12</age>
<sex>female</sex>
<score>89</score>
</student>
<student number="s004">
<name>zhaoliu</name>
<age>24</age>
<sex>male</sex>
<score>78</score>
</student>
</students>
number、name、age、sex、score這些元素,應該被封裝成一個Student類,這個Student類如下:
package com.heisejiuhuche.pull;
public class Student {
/*
* 私有的成員屬性,都是XML文件當中的元素
*/
private String number;
private String name;
private int age;
private String sex;
private double score;
/*
* 帶參數的構造方法,用於封裝XML文件當中解析出來的數據
*/
public Student(String number, String name, int age, String sex, double score) {
super();
this.number = number;
this.name = name;
this.sex = sex;
this.age = age;
this.score = score;
}
/*
* 複寫toString方法
*/
@Override
public String toString() {
return "Student [number=" + number + ", name=" + name + ", age=" + age
+ ", score=" + score + "]";
}
/*
* 一些列的get set方法
*/
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
/*
* 空參數構造方法
*/
public Student() {
super();
}
}
寫好了Student類,下面要做的就是解析XML文件。解析的整體思路就是利用Pull解析器的getEventType()方法,獲取解析器當前的標籤狀態,判斷該標籤是起始標籤還是結束標籤,做出相應的動作。如果是起始標籤,又可以使用解析器的getName()方法獲取標籤名,通過判斷標籤名,再進行進一步的操作。代碼中有詳細的註釋。
示例代碼:
package com.heisejiuhuche.pull;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
/**
* 這個類將解析student.xml,將student元素的每個屬性,封裝到Student類當中
* 然後將每個Student對象存入集合
* 最後打印這個集合驗證每個對象解析並存儲成功
*
* 在實現過程中最重要的是明確解析的每一步應該做出什麼樣的動作,例如哪一步該
* 初始化集合,哪一步該封裝數據,哪一步該存儲對象,在註釋中有一一說明
* @author jeremy
*
*/
public class PullRead {
public static void main(String[] args) throws Exception {
//得到PullParser解析器工廠
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
//從工廠得到解析器parser
XmlPullParser parser = factory.newPullParser();
//設置parser解析器的輸入流,關聯需要解析的xml文件,並指定解析編碼爲utf-8
parser.setInput(new FileInputStream("student.xml"), "utf-8");
//聲明空集合,用於以後存儲Student對象
List<Student> list = null;
//聲明Student對象,用於封裝解析出來的數據
Student student = null;
//Pull是基於事件驅動,所以首先要獲得解析器的事件類型
int eventType = parser.getEventType();
//如果事件類型不等於XmlPullParse類的結束標籤這個靜態常量,就執行循環體
while(eventType != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName(); //獲得此時此刻解析器指針位置的標籤名
switch (eventType) { //switch語句對事件類型進行判斷,做出相應動作
case XmlPullParser.START_TAG: //如果當前標籤是開始標籤
/*
* 判斷如果是students根標籤,即初始化list集合
*/
if(tagName.equals("students")) {
list = new ArrayList<Student>();
}
/*
* 如果是student元素,就初始化Student對象,獲取student元素的
* 唯一屬性:number,並賦值給student的number屬性
*/
if(tagName.equals("student")) {
student = new Student();
String number = parser.getAttributeValue(0);
student.setNumber(number);
}
/*
* 一下以此類推,判斷標籤名,然後調用parser解析器的nextText()方法
* 獲取文本值,並賦值給Student類的相應屬性
*/
if(tagName.equals("name")) {
String name = parser.nextText();
student.setName(name);
}
if(tagName.equals("age")) {
String age = parser.nextText();
student.setAge(Integer.parseInt(age));
}
if(tagName.equals("sex")) {
String sex = parser.nextText();
student.setSex(sex);
}
if(tagName.equals("score")) {
String score = parser.nextText();
student.setScore(Double.parseDouble(score));
}
break;
case XmlPullParser.END_TAG: //如果標籤狀態爲結束標籤
/*
* 如果是student元素的結束標籤,就將一個封裝好的Student對象
* 存儲到list集合中,並將student對象置爲空,以便下次使用
* 置空操作爲可選
*/
if(tagName.equals("student")) {
list.add(student);
student = null;
}
break;
default:
break;
}
//parser解析器讀完一行之後,控制解析器繼續往下讀一行
parser.next();
//並獲得當前事件類型
eventType = parser.getEventType();
}
//打印整個集合
System.out.println(list);
}
}
程序輸出結果:
[Student [number=s007, name=zs, age=14, score=91.0], Student [number=s002, name=wangwu, age=24, score=87.0], Student [number=s003, name=lisi, age=12, score=89.0], Student [number=s004, name=zhaoliu, age=24, score=78.0]]
解析並封裝成功~
除了解析,Pull也提供了寫如一個XML文件的方法,通過XmlSerializer對象的一系列方法(序列化)實現。寫入的方式相對簡單,就是調用startDocument(),startTag(),text()等方法,創建標籤即文本內容即可。下面的代碼將創建一個名爲stu.xml文件。
示例代碼:
package com.heisejiuhuche.pull;
import java.io.FileOutputStream;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
public class PullWrite {
public static void main(String[] args) throws Exception {
//創建解析器工廠
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
//獲取XmlSerializer對象
XmlSerializer serializer = factory.newSerializer();
//設置serializer對象的輸出流,關聯要輸出的文件,並指定輸出編碼爲utf-8
serializer.setOutput(new FileOutputStream("stu.xml"), "utf-8");
//startDocument方法接收兩個參數,分別是xml文檔聲明的encoding的值和standalone的值
serializer.startDocument("utf-8", true);
/*
* 創建students根元素,以下方法中的第一個參數null是名稱空間,這裏沒有名稱空間,故爲null
* 以此類推,創建student元素,創建name元素,並賦值
* 要注意的一點就是,寫完start方法,就馬上寫end方法,然後將子元素寫在start和end之間
* 就像他們出現在最終xml文件中的格式一樣
*/
serializer.startTag(null, "students");
serializer.startTag(null, "student");
serializer.startTag(null, "name");
serializer.text("lisi");
serializer.endTag(null, "name");
serializer.startTag(null, "age");
serializer.text("18");
serializer.endTag(null, "age");
serializer.startTag(null, "sex");
serializer.text("male");
serializer.endTag(null, "sex");
serializer.startTag(null, "score");
serializer.text("98");
serializer.endTag(null, "score");
serializer.endTag(null, "student");
serializer.endTag(null, "students");
serializer.endDocument();
}
}
生成的stu.xml文檔:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<students>
<student>
<name>lisi</name>
<age>18</age>
<sex>male</sex>
<score>98</score>
</student>
</students>
2.DOM4J解析器使用步驟
老大哥來了,使用起來真的是方便。DOM4J集合了SAX和DOM的思想,在關聯解析文件的時候,用的是SAXReader對象,而對XML進行增刪改查的過程,用的又是DOM的思想。不愧是解析思想的集大成者。廢話不多說,用代碼說明一下DOM4J的使用,真的是簡單~
2.1DOM4J解析XML文件的步驟
第一步:
創建SAXReader對象
SAXReader reader = new SAXReader();
第二步:
關聯解析文件,得到該文件的Document對象,這是DOM思想的開始
Document document = reader.read("文件路徑");
2.2DOM4J回寫(這裏對於回寫就不做說明啦)的步驟
DOM4J的回寫是通過XMLWriter對象,配合OutputFormat對象實現的,簡單看一下吧,用起來很方便。
第一步:
創建OutputFormat對象,這個對象是用於格式化輸出的XML文件,通過兩個靜態方法createPrettyPrint()和createCompactFormat()來創建格式化的輸出和緊湊的輸出;格式化的就是我們一般看到的XML格式,compact的格式就是將XML文件輸出在一行,供計算機閱讀;這裏我們就是用pretty格式啦!
OutputFormat format = OutputFormat.createPrettyPrint();
第二步:
創建XMLWriter對象,這個對象接收OutputStream和OutputFormat對象作爲參數
XMLWriter writer = new XMLWriter(new FileOutputStream(filePath), format);
第三步:
回寫修改之後的XML對象,這個XML對象現在是以Document的形式存在於內存,調用write方法將他寫回到XML文件
writer.write(document);
第四步:
由於XMLWriter要開流,所以要關閉資源
writer.close();
完成啦~
下面就用具體的代碼來闡述如何用DOM4J來對XML文件進行增刪改查的操作。先來看看Student.xml這個需要被操作的文件。
Student.xml
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student number="s007">
<name>zs</name>
<age>14</age>
<sex>male</sex>
<score>91</score>
</student>
<student number="s002">
<name>wangwu</name>
<age>24</age>
<sex>male</sex>
<score>87</score>
</student>
<student number="s003">
<name>lisi</name>
<age>12</age>
<sex>female</sex>
<score>89</score>
</student>
<student number="s004">
<name>zhaoliu</name>
<age>24</age>
<sex>male</sex>
<score>78</score>
</student>
</students>
由於大部分方法都要獲取Document對象,也都要回寫Document對象,所以先將這兩個方法封裝到一個工具類中,方便調用。
DOM4JUtils工具類
package com.heisejiuhuche.dom4j;
import java.io.FileOutputStream;
import java.io.IOException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
/**
* DOM4JUtils工具類,用於獲取Document對象和回寫操作
* @author jeremy
*
*/
public class Dom4JUtils {
//私有化構造方法,不能創建其對象
private Dom4JUtils() {}
//獲取Document對象的靜態方法,獲取步驟上文已經說明
public static Document getDocument(String filePath) throws DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(filePath);
return document;
}
//回寫的靜態方法,回寫步驟上文已經說明
public static void write2File(Document document, String filePath) throws IOException {
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(new FileOutputStream(filePath), format);
writer.write(document);
writer.close();
}
}
寫好了工具類,就可以開始對XML文件進行解析了,下面的代碼中運用了JUnit Test的方式對每一個方法進行測試,就不需要寫一個main方法,省去了在main方法中一個一個調用的麻煩。
JUnit小技巧:
在方法上面,加上@Test註解(下面有示例),然後在報錯行按下Ctrl+1導入JUnit包,接着就可以雙擊方法名——>Run as——>JUnit Test即可運行該方法;如果看到下圖的綠色條,就是方法運行成功,沒有報錯
package com.heisejiuhuche.dom4j;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.junit.Test;
public class Dom4J {
String filePath = "student.xml";
/**
* 查詢
* 1.查詢第一個student元素的number 屬性值
* 2.查詢第二個student元素的name子元素的 文本
* @throws DocumentException
*/
@Test
public void find() throws DocumentException {
//獲取document對象
Document document = Dom4JUtils.getDocument(filePath);
//獲取document對象的跟標籤,即students標籤
Element rootElement = document.getRootElement();
//通過students跟標籤獲取student標籤,element方法可以獲取指定的標籤
Element student = rootElement.element("student");
//通過attribute方法通過指定的屬性名獲取屬性,通過getText方法獲取屬性值
System.out.println(student.attribute("number").getText());
//通過elements方法獲取所有student元素的集合,通過get方法,獲取集合中的第二個student元素
Element student2 = (Element)rootElement.elements("student").get(1);
//打印第二個元素的name的值
System.out.println(student2.element("name").getText());
}
/**
* 添加
* 1.給第一個student元素添加屬性 country=China
* 2.給第二個student元素添加子元素<anotherName>小四</anotherName>
*/
@Test
public void add() throws Exception {
//獲取document
Document document = Dom4JUtils.getDocument(filePath);
//獲取根元素
Element rootElement = document.getRootElement();
//獲取第一個student元素,通過addAttribute方法增加屬性,方法接收屬性名,屬性值兩個參數
rootElement.element("student").addAttribute("country", "China");
//獲取第二個student元素
Element student2 = (Element)rootElement.elements("student").get(1);
//通過createElement方法創建指定名稱的子元素標籤
Element anotherName = DocumentHelper.createElement("anotherName");
//通過setText()方法設定標籤值
anotherName.setText("xiaosi");
//通過add方法將新的標籤添加到第二個stduent元素下
student2.add(anotherName);
//修改完的document對象還處於內存,要調用工具類的回寫方法將document對象回寫到文件
Dom4JUtils.write2File(document, filePath);
}
/**
* 添加
*
給students添加一個student元素
<student number="s004">
<name>zhaoliu</name>
<age>24</age>
<sex>male</sex>
<score>99</score>
</student>
*
*/
@Test
public void add2() throws Exception {
Document document = Dom4JUtils.getDocument(filePath);
Element rootElement = document.getRootElement();
//parseText方法,可以將xml文本直接解析爲xml標籤和值,很方便,該方法返回一個Doucment對象
Document student = DocumentHelper.parseText("<student number='s004'><name>zhaoliu</name><age>24</age><sex>male</sex><score>99</score></student>");
//注意,這裏要獲取這個document對象的根元素,才能得到新添加的student元素對象
Element studentRoot = student.getRootElement();
//將新的student元素添加到students根元素下
rootElement.add(studentRoot);
//回寫
Dom4JUtils.write2File(document, filePath);
}
/**
* 刪除
* 1.刪除第一個student元素的country
* 2.刪除第二個student元素的<score>
*/
@Test
public void remove() throws Exception {
Document document = Dom4JUtils.getDocument(filePath);
Element rootElement = document.getRootElement();
Element student = rootElement.element("student");
//通過attribute方法得到指定明名稱的屬性,即country屬性
Attribute attr = student.attribute("country");
//通過remove方法刪除該屬性
student.remove(attr);
//獲得第二個學生對象
Element student2 = (Element)rootElement.elements("student").get(1);
//刪除score標籤
student2.remove(student2.element("score"));
//修改完成後回寫生效
Dom4JUtils.write2File(document, filePath);
}
/**
* 修改
* 1.修改第一個student元素的屬性 number=s007
* 2.修改第二個student元素的sex子元素 爲 male
*/
@Test
public void update() throws Exception {
Document document = Dom4JUtils.getDocument(filePath);
Element rootElement = document.getRootElement();
Element student = rootElement.element("student");
//addAttribute方法有個特點,如果屬性不存在,則添加,如果屬性存在,則修改該屬性
student.addAttribute("number", "s007");
//獲取第二個student對象
Element student2 = (Element)rootElement.elements("student").get(1);
//獲取sex元素並將值修改爲male
student2.element("sex").setText("male");;
//回寫生效
Dom4JUtils.write2File(document, filePath);
}
}
三、小案例
這個案例實現了一個簡易的商品管理系統,用戶可以選擇需要的操作,完成對商品XML文件的增刪改查操作。有興趣就看看吧,不想寫註釋了。。。有點累咯大家見諒~大致的實現思想就是基於DOM4J的解析和回寫。當然了,第一次啓動程序的時候,需要先添加一些列的商品,然後在進行其他操作。代碼裏沒有很嚴謹,沒有對輸入做校驗,所以才叫簡易系統嘛哈哈,我就不爲難自己了~
pro.xml
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product id="000000000002">
<name>shanddd</name>
<price>666.66666</price>
<number>666</number>
<description>shangpinxiuxiuxiu</description>
</product>
<product id="001">
<name>shangpin01</name>
<price>11.11</price>
<number>111</number>
<description>shangpin01</description>
</product>
<product id="003">
<name>shangpin03</name>
<price>888.88</price>
<number>888</number>
<description>shangpin03xiugai</description>
</product>
<product id="005">
<name>shangpin05</name>
<price>55.55</price>
<number>555</number>
<description>shangpin05</description>
</product>
<product id="006">
<name>shagpin06</name>
<price>66.66</price>
<number>666</number>
<description>shangpin06</description>
</product>
<product id="007">
<name>shangpin07</name>
<price>77.77</price>
<number>777</number>
<description>shangpin07</description>
</product>
<product id="008">
<name>shangpin08</name>
<price>88.88</price>
<number>888</number>
<description>shangpin08</description>
</product>
</products>
ProductManagingSystem
package com.heisejiuhuche.dom4j;
import java.util.List;
import java.util.Scanner;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.junit.Test;
public class ProductManagingSystem {
static Scanner scanner = new Scanner(System.in);
static String filePath = "pro.xml";
public static void main(String[] args) {
while(true) {
userInterface();
}
}
//請選擇您所需的操作:
/*
<products>
<product>
<id></id>
<name></name>
<price></price>
<number></number>
<description></description>
</product>
</products>
*
*
*/
private static void userInterface() {
System.out.println("歡迎使用黑馬64期產品管理系統!");
System.out.println("請選擇您所需的操作:");
System.out.println("1.查詢商品信息");
System.out.println("2.添加商品信息");
System.out.println("3.刪除商品信息");
System.out.println("4.更改商品信息");
System.out.println("5.退出");
int option = scanner.nextInt();
switch (option) {
case 1:
showProductInfo();
break;
case 2:
addProductInfo();
break;
case 3:
removeProductInfo();
break;
case 4:
updateProductInfo();
break;
case 5:
System.out.println("謝謝使用!");
System.exit(0);
break;
default:
break;
}
}
private static void updateProductInfo() {
try {
System.out.println("請輸入商品名稱:");
String name = scanner.next();
// System.out.println("請輸入商品id:");
// String id = scanner.next();
System.out.println("請輸入商品價格:");
double price = scanner.nextDouble();
System.out.println("請輸入商品數量:");
int number = scanner.nextInt();
System.out.println("請輸入商品介紹:");
String description = scanner.next();
Document document = Dom4JUtils.getDocument(filePath);
Element rootElement = document.getRootElement(); //products
List<Element> proList = rootElement.elements("product"); //all product element
for(Element ele : proList) {
String proName = ele.element("name").getText(); //ele is a product element
if(proName.equals(name)) {
ele.element("price").setText(price + "");
ele.element("number").setText(number + "");
ele.element("description").setText(description);
}
}
Dom4JUtils.write2File(document, filePath);
} catch(Exception e) {
e.printStackTrace();
}
}
private static void removeProductInfo() {
try {
System.out.println("請輸入商品名稱:");
String name = scanner.next();
Document document = Dom4JUtils.getDocument(filePath);
Element rootElement = document.getRootElement();
List<Element> proList = rootElement.elements("product");
for(Element ele : proList) {
String proName = ele.element("name").getText(); //ele is a product element
if(proName.equals(name)) {
rootElement.remove(ele);
}
}
Dom4JUtils.write2File(document, filePath);
} catch(Exception e) {
e.printStackTrace();
}
}
private static void addProductInfo() {
try {
System.out.println("請輸入商品名稱:");
String name = scanner.next();
System.out.println("請輸入商品id:");
String id = scanner.next();
System.out.println("請輸入商品價格:");
double price = scanner.nextDouble();
System.out.println("請輸入商品數量:");
int number = scanner.nextInt();
System.out.println("請輸入商品介紹:");
String description = scanner.next();
Product pro = new Product(id, name, price, number, description);
Document rootDocument = Dom4JUtils.getDocument(filePath);
Element rootElement = rootDocument.getRootElement();
Document document = DocumentHelper.parseText("<product id='" + pro.getId() + "'><name>"
+ pro.getName() + "</name><price> "
+ pro.getPrice() + " </price><number> "
+ pro.getNumber() + " </number><description> "
+ pro.getDescription()
+ " </description></product>");
Element proElement = document.getRootElement();
rootElement.add(proElement);
Dom4JUtils.write2File(rootDocument, filePath);
} catch(Exception e) {
e.printStackTrace();
}
}
private static void showProductInfo() {
try {
Document document = Dom4JUtils.getDocument(filePath);
Element rootElement = document.getRootElement();
List<Element> list = rootElement.elements("product");
for(Element eles : list) {
System.out.println(eles.attribute("id").getName() + ": " + eles.attributeValue("id"));
List<Element> eleList = eles.elements();
for(Element e : eleList) {
System.out.println(e.getName() + ": " + e.getText());
}
System.out.println("======================================");
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
KEEP CALM AND CARRY ON~