Java xml 工具 JDOM 使用详解.
一, 什么是JDOM工具
在編程中, 我們往往需要一些配置數(shù)據(jù),? 這些值我們一般不會(huì)hardcode在代碼中的. 而是寫(xiě)在配置文件.
在Java世界中, 我們通常會(huì)把配置變量寫(xiě)在xml文件中.
而xml 也不只是配置文件這么簡(jiǎn)單, 實(shí)際上, 我們可以把一些用戶數(shù)據(jù)寫(xiě)在xml里.
這樣, xml就如一個(gè)微型簡(jiǎn)便的數(shù)據(jù)庫(kù).
簡(jiǎn)單來(lái)講, 我們可以將一個(gè)對(duì)象的所有屬性(Attribute)寫(xiě)在xml里, 也可以通過(guò)讀取xml里的數(shù)據(jù)構(gòu)造1個(gè)對(duì)象出來(lái).
Java里有若干個(gè)讀寫(xiě)xml的工具, 它們分別是
DOM:? 方便,? 但是會(huì)將整個(gè)xml所有數(shù)據(jù)一次過(guò)讀進(jìn)內(nèi)存.
SAX:?? 內(nèi)存占用少, 使用起來(lái)稍稍復(fù)雜.
JDOM: 基于SAX編寫(xiě)的xml工具, 集合上面兩個(gè)工具的優(yōu)點(diǎn).
本文主要介紹JDOM.
二, 安裝JDOM工具.
JDOM實(shí)際上只是1個(gè)jar包, 而不是1個(gè)eclipse插件.
JDOM的最新版本是2.0.5 , 也就是JDOM2 了..
可以在如下網(wǎng)址中下載.
http://www.jdom.org/downloads/
下載得到1個(gè)zip包, 解壓出 jdom-2.0.5.jar 文件.
把它寫(xiě)進(jìn)CLASSPATH變量里就可以使用了.
三,XML各個(gè)關(guān)鍵字簡(jiǎn)單介紹
在正式開(kāi)始介紹JDOM 用法時(shí), 有必要簡(jiǎn)單講下xml各個(gè)組成部分.
如下圖:
上面是一個(gè)常見(jiàn)的XML圖, 它實(shí)際上存儲(chǔ)了兩個(gè)Student對(duì)象的數(shù)據(jù).
下面簡(jiǎn)單解釋一下xml的常見(jiàn)名詞:
3.1 頭部信息
頭部信息包含了xml的版本信息, 以及編碼方法.
頭部信息是每個(gè)xml文件都必須備有的.
3.2 Element.
我們可以把Element(元素) 看成xml的一個(gè)基本單元.
1個(gè)Element的常見(jiàn)寫(xiě)法如下:
<A>abc</A>, 有點(diǎn)類似與html,?? Element的值被兩個(gè)標(biāo)簽(Tag)包住.
3.3 name of Element.
Element標(biāo)簽里的值就算是這個(gè)Element的名字了,? 這個(gè)值很重要, JDOM檢索Element基本上是靠它來(lái)檢索(而不是依照Value來(lái)檢索)>Name 是Element里的1個(gè)必備組成部分.
也就是每1個(gè)Element都必須有1個(gè)name啦
3.4 Value of Element.
被name標(biāo)簽(tag),包住的部分就是value了.注意, value并不是Element的必備部分,? 例如上圖例子中,? Element "fox:Student"? 就沒(méi)有Value, 但是它有3個(gè)子Element.
3.5 Child
child就是子Element, 例如上圖例子中, id, name, age 都是 Student的child,? 而 Student 是 StudentRecords的Child.
注意, child只是一層的, 也就說(shuō) id, name,age 并不是 StudentRecords的child啊.
3.6 Children
1個(gè)Element的所有child的集合,? 例如上圖StudentRecords 有兩個(gè)child,? 而這兩個(gè)child各自又有3個(gè)child(id, name, student),
3.7 Decsendant
1個(gè)Element 的后代, 子孫集合
相對(duì)于child來(lái)將, 這個(gè)概念就是多層的,
就如一個(gè)Tree結(jié)構(gòu), 所有子孫節(jié)點(diǎn)(node) 在Descendant的集合里面.
3.8 Root Element
就是根節(jié)點(diǎn)的意思啦. 上圖例子中的StudentRecords就是1個(gè)Root Element.
在JDOM中,? 1個(gè)xml文件只能有1個(gè)Root Element,? 所有其他Element都在Root Element的 Descendant 集合中.
3.9 NameSpace
首先Namespace不是必須的.
有時(shí)為了區(qū)分同名的節(jié)點(diǎn), 可以為一些節(jié)點(diǎn)加入另1個(gè)屬性, 就是所謂的命名空間.
實(shí)際上, 命名空間的存在可以將xml里的節(jié)點(diǎn)依照命名空間分成若干個(gè)部分.
也就系講, 令到xml中, 某些element是屬于這個(gè)Namespace,? 某些element是另1個(gè)Namespace.
而且Namespace可以任意加在任何1個(gè)Element里.
在JDOM的世界里,
Namespace是1個(gè)重要的filter條件.
也就是說(shuō), 如果1個(gè)Element有Namespace,?? 如果只靠這個(gè)Element的name是filter不出來(lái)的, 必須加上命名空間才能得到這個(gè)Element.
命名空間作為element的一個(gè)屬性(Attribute)存在.
通常來(lái)講, 命名空間可以寫(xiě)成如下格式:
xmlns:ggl="www.google.com"
其中, xmlns是1個(gè)關(guān)鍵字, 表示后面的是1個(gè)命名空間.
www.google.com是1個(gè)URL, 作為命名空間的URL了. 因?yàn)镮nternet上每個(gè)網(wǎng)址都是唯一的.
注意這個(gè)URL只是Element的一個(gè)標(biāo)石.? 而不是說(shuō)xml會(huì)去訪問(wèn)這個(gè)網(wǎng)址.
ggl 這個(gè)就是命名空間的Prefix, 可以理解成命名空間的1個(gè)簡(jiǎn)稱.
例如上面例子中.
<ggl:Student xmlns:ggl="www.google.com"><ggl:id>1</ggl:id><ggl:name>Jack</ggl:name><ggl:age>14</ggl:age></ggl:Student>有4個(gè)element
都是屬于1個(gè)命名空間, 而URL的值只定義一次就夠了,? 后面的都可以用命名空間來(lái)代替.
上面的只是1個(gè)例子.
實(shí)際上我們不需要為xml的每1個(gè)element都加上命名空間, 只需要在關(guān)鍵的element加上就ok了.
四,利用JDOM 把對(duì)象數(shù)據(jù)寫(xiě)入1個(gè)新文件.
這個(gè)小節(jié)中, 會(huì)說(shuō)明
1. 如何創(chuàng)建1個(gè)xml.
4.1 Student類
首先你要有個(gè)對(duì)象是吧.. 我們就把1個(gè)student類寫(xiě)出來(lái)
它有3個(gè)成員id, name, 和 age
class Student{private int id;private String name;private int age;public Student(int id, String name, int age){this.setId(id);this.setName(name);this.setAge(age);}public int getId() {return id;}public void setId(int id) {this.id = id;}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;}@Overridepublic String toString(){return "Student: " + this.getId() + ", " + this.getName() + ", " + this.getAge();}4.2 客戶端代碼:
public class Testxml2 {public static void Create(){Student sd1 = new Student(1,"Jack",14);Student sd2 = new Student(2,"Mike",15);Student sd3 = new Student(3,"Paul",15);// new a xml fileDocument docJdom = new Document();//Namespace(not must, it's optional, to indicate elements with same nameNamespace np1 = Namespace.getNamespace("ggl", "www.google.com");Namespace np2 = Namespace.getNamespace("fox","www.firefox.com");// new a new root nodeElement eRoot = new Element("StudentRecords");docJdom.addContent(eRoot);//add sd1 nodeElement nodeSd1 = new Element("Student",np1);nodeSd1.setNamespace(np1);//idElement nodeId1 = new Element("id");nodeId1.addContent(""+sd1.getId());//nameElement nodeName1 = new Element("name");nodeName1.addContent(""+sd1.getName());//ageElement nodeAge1 = new Element("age");nodeAge1.addContent(""+sd1.getAge());nodeSd1.addContent(nodeId1);nodeSd1.addContent(nodeName1);nodeSd1.addContent(nodeAge1);eRoot.addContent(nodeSd1);//add sd2 nodeElement nodeSd2 = new Element("Student",np1);nodeSd2.setNamespace(np1);//idElement nodeId2 = new Element("id");nodeId2.addContent(""+sd2.getId());//nameElement nodeName2 = new Element("name");nodeName2.addContent(""+sd2.getName());//ageElement nodeAge2 = new Element("age");nodeAge2.addContent(""+sd2.getAge());nodeSd2.addContent(nodeId2);nodeSd2.addContent(nodeName2);nodeSd2.addContent(nodeAge2);eRoot.addContent(nodeSd2);//add sd3 nodeElement nodeSd3 = new Element("Student",np2);nodeSd3.setNamespace(np2);//idElement nodeId3 = new Element("id");nodeId3.addContent(""+sd3.getId());//nameElement nodeName3 = new Element("name");nodeName3.addContent(""+sd3.getName());//ageElement nodeAge3 = new Element("age");nodeAge3.addContent(""+sd3.getAge());nodeSd3.addContent(nodeId3);nodeSd3.addContent(nodeName3);nodeSd3.addContent(nodeAge3);eRoot.addContent(nodeSd3); OutputXML(docJdom,"/home/gateman/Studies/JavaDesignPattern/xml/02.xml");System.out.println("Done!");}private static void OutputXML(Document dXml, String sFilenm){XMLOutputter xot = new XMLOutputter();xot.setFormat(Format.getPrettyFormat());try{FileWriter fwXML = new FileWriter(sFilenm);xot.output(dXml, fwXML);}catch(Exception e){e.printStackTrace();}} }代碼其實(shí)不難看懂.
1. 首先創(chuàng)建3個(gè)student對(duì)象
2. 創(chuàng)建兩個(gè)命名空間
3. 建立1個(gè)Document對(duì)象(JDOM中, Document指的是xml文件本身, 一個(gè)xml文件對(duì)應(yīng)1個(gè)Document對(duì)象.
4. 建立1個(gè)Root Element對(duì)象.
5. 根據(jù)3個(gè)對(duì)象依次建立3個(gè)element, 然后把這個(gè)3個(gè)element添加到Root Element里, 作為它的子element.
6. 利用XMLOutputter把Document對(duì)象寫(xiě)出到硬盤(pán). 注意這個(gè)方法是覆蓋已存在文件, 并不是append到文件末尾.
生成的xml文件是這樣的:
gateman@TPEOS xml $ cat 02.xml <?xml version="1.0" encoding="UTF-8"?> <StudentRecords><ggl:Student xmlns:ggl="www.google.com"><id>1</id><name>Jack</name><age>14</age></ggl:Student><ggl:Student xmlns:ggl="www.google.com"><id>2</id><name>Mike</name><age>15</age></ggl:Student><fox:Student xmlns:fox="www.firefox.com"><id>3</id><name>Paul</name><age>15</age></fox:Student> </StudentRecords>五,把寫(xiě)入xml的方法作為1個(gè)接口
的確, 上面的代碼又長(zhǎng)又臭, 而且重復(fù)代碼實(shí)在太多,.
所以, 我們一般會(huì)把寫(xiě)入xml的動(dòng)作放入類的代碼中, 作為1個(gè)接口.
5.1 XMLable 接口:
import org.jdom2.Element; import org.jdom2.Namespace; public interface XMLable {public Element buildElement(Namespace np); }
只需定義1個(gè)返回1個(gè)Element對(duì)象的方法.
5.2 Student 類:
讓Student類去實(shí)現(xiàn)這個(gè)接口.
import org.jdom2.Element; import org.jdom2.Namespace;public class Student implements XMLable {private int id;private String name;private int age;public Student(int id, String name, int age){this.setId(id);this.setName(name);this.setAge(age);}public int getId() {return id;}public void setId(int id) {this.id = id;}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;}@Overridepublic String toString(){return "Student: " + this.getId() + ", " + this.getName() + ", " + this.getAge();}@Overridepublic Element buildElement(Namespace np) {Element node = new Element("Student",np);node.setNamespace(np);//idElement nodeId = new Element("id");nodeId.addContent(""+this.getId());//nameElement nodeName = new Element("name");nodeName.addContent(""+this.getName());//ageElement nodeAge = new Element("age");nodeAge.addContent(""+this.getAge());node.addContent(nodeId);node.addContent(nodeName);node.addContent(nodeAge);return node;} }5.3 XMLExport類
把Output方法封裝1個(gè)類的靜態(tài)函數(shù):
import org.jdom2.Document; import org.jdom2.output.XMLOutputter; import org.jdom2.output.Format; import java.io.FileWriter;public class XMLExport {public static void OutputXML(Document dXml, String sFilenm){XMLOutputter xot = new XMLOutputter();xot.setFormat(Format.getPrettyFormat());try{FileWriter fwXML = new FileWriter(sFilenm);xot.output(dXml, fwXML);}catch(Exception e){e.printStackTrace();}} }5.4 客戶端代碼:
這樣一來(lái), 客戶端代碼就相當(dāng)簡(jiǎn)潔了: Student sd1 = new Student(1,"Jack",14);Student sd2 = new Student(2,"Mike",15);Student sd3 = new Student(3,"Paul",15);// new a xml fileDocument docJdom = new Document();//Namespace(not must, it's optional, to indicate elements with same nameNamespace np1 = Namespace.getNamespace("ggl", "www.google.com");Namespace np2 = Namespace.getNamespace("fox","www.firefox.com");// new a new root nodeElement eRoot = new Element("StudentRecords");docJdom.addContent(eRoot);eRoot.addContent(sd1.buildElement(np1));eRoot.addContent(sd2.buildElement(np1));eRoot.addContent(sd3.buildElement(np2));XMLExport.OutputXML(docJdom,"/home/gateman/Studies/JavaDesignPattern/xml/04.xml");六, 根據(jù)條件從XML文件中獲取Element對(duì)象.
首先講一句,
JDOM中沒(méi)有根據(jù)xml文件中某個(gè)Element的值(Value)返回其Element的方法. 不要把它當(dāng)做sql數(shù)據(jù)庫(kù)那么強(qiáng)大啦.
基本上常用filter方法有種.
1.是根據(jù)名字來(lái)filter.
2.是根據(jù)Namespace來(lái)filter.
上面的filter返回的都是1個(gè)集合. 也就是說(shuō)你還需要遍歷它們的子Element才能獲得真正需要的Element.
而且當(dāng)1個(gè)Element具有Namespace, 當(dāng)屏它的Name是filter不出來(lái)的, 必須用Name和Namespace一起來(lái)filter.
一步一步來(lái)..
6.1 根據(jù)文件名獲得Document對(duì)象.
這個(gè)是前提.
代碼也很簡(jiǎn)單, 需要用到類SaxBuilder(也在JDOM包中)
6.2 獲取Root Element
JDOM中Root Element只會(huì)有1個(gè).
而JDOM也提供1個(gè)獲取Root Element的方法
當(dāng)然前提是你有1個(gè)Document對(duì)象啦.
6.3 getChild()方法
這個(gè)方法返回1個(gè)Element中的第1個(gè)子Element.
注意, 不包括孫Element哦.
它有兩個(gè)重載方法.
getChild(String name)? -> 返回第1個(gè)名字是name.而且沒(méi)有Namespace的子Element,
6.4 getChildren()方法
返回1個(gè)Element的所有子Element的集合,? 這個(gè)集合是1個(gè)java.util.List對(duì)象.注意, 也不包括孫Element, 只有1層哦.
也有兩個(gè)重載方法
getChildren(String name)? -> 返回所有名字是name, 而且沒(méi)有Namespace的子Element的對(duì)象集合,
6.5 getDescendant()方法
這個(gè)就厲害了.? Descendant是后代的意思, 這個(gè)方法能返回1個(gè)Element的所有子孫后代的Element集合. 注意這個(gè)集合是1個(gè)org.jom2.util.IteratorIterable<Element>?對(duì)象的集合
它也有1個(gè)重載方法:
getDescendant(Filter ft)? ->? 根據(jù)條件ft(1個(gè)Filter對(duì)象) 來(lái)獲得所有符合條件的后代Element集合. 下面的例子會(huì)用到這個(gè)方法
這個(gè)方法是真正上的Filter了, 因?yàn)樗鼙闅v1個(gè)Element的所有子孫.
也就說(shuō)如過(guò)用它來(lái)遍歷RootElement, 就相當(dāng)于遍歷整個(gè)xml文件!
6.6 Filter 類
org.jdom2.Filter是Jdom2包里的一個(gè)抽象類.
最常用的子類是ElementFilter.?
它的對(duì)象通常用來(lái)作為getDescendant 方法的參數(shù).
new ElementFilter(name, np) 代表構(gòu)造1個(gè)用名字name和命名空間np組成的Filter對(duì)象
6.7 getParentElement()方法
獲得1個(gè)非RootElement的的父Element.注意1個(gè)Element只會(huì)有1個(gè)父Element啦.
6.8 獲取上面xml文件中id為1的Element的例子代碼.
艾呀這篇文章太冗長(zhǎng)了.
上面的xml是這樣的:
gateman@TPEOS xml $ cat 04.xml <?xml version="1.0" encoding="UTF-8"?> <StudentRecords><ggl:Student xmlns:ggl="www.google.com"><id>1</id><name>Jack</name><age>14</age></ggl:Student><ggl:Student xmlns:ggl="www.google.com"><id>2</id><name>Mike</name><age>15</age></ggl:Student><fox:Student xmlns:fox="www.firefox.com"><id>3</id><name>Paul</name><age>15</age></fox:Student> </StudentRecords>它包含3個(gè)Student對(duì)象的數(shù)據(jù)
假如我想獲得id等于2的StudentElement, 可以這樣寫(xiě):
還是先說(shuō)幾句
首先, id = 2中, 2是1個(gè)值, 上面說(shuō)過(guò)了, JDOM是不支持用值來(lái)檢索的.
id是1個(gè)名字, 可以用"id"來(lái)檢索.? 該xml中有3個(gè)名字是"id"的Element(分別屬于3個(gè)Student Element),? 它們都沒(méi)有Namespace.
所以只需要
利用RootElement的? getDescendant("id")得到3個(gè)id Element的集合, 然后再去遍歷它們的值..
代碼如下:
SAXBuilder sbd = new SAXBuilder();Document docJdom = null;File xmlfile = null;try{xmlfile = new File("/home/gateman/Studies/JavaDesignPattern/xml/02.xml");docJdom = sbd.build(xmlfile);}catch(Exception e){e.printStackTrace();}Element rootElement = docJdom.getRootElement();//all the element named "id" & without namespaceIteratorIterable<Element> elist = rootElement.getDescendants(new ElementFilter("id"));Iterator it = elist.iterator();Element eId2 = null;//loopwhile(it.hasNext()){Element em = (Element)(it.next());if (2 == new Integer(em.getChild("id").getValue()).intValue()){eId2 = em.getParentElement(); }}上面的eId2就是想要得到的Element對(duì)象.
七, 把1個(gè)Element封裝成1個(gè)類
例如上面我獲得了id=2 的element, 如何把這個(gè)element封裝成1個(gè)類?
下面是1個(gè)利用簡(jiǎn)單工廠模式的例子把構(gòu)造類方法寫(xiě)在1個(gè)產(chǎn)品里的方法里:
7.1 BuildXmlObj 接口
import org.jdom2.Element; public interface BuildXmlObj {public Object buildObj(Element elm); }它具有1個(gè)根據(jù)Element構(gòu)造1個(gè)對(duì)象的方法.
7.2 BuildXmlStudent 類
實(shí)現(xiàn)上面的接口
import org.jdom2.Element;public class BuildXmlStudent implements BuildXmlObj {@Overridepublic Object buildObj(Element em) {try{return new Student(new Integer(em.getChild("id").getValue()).intValue(), em.getChild("name").getValue(),new Integer(em.getChild("age").getValue()).intValue()); }catch(Exception e){e.printStackTrace();return null;}} }7.3 工廠 類
public class FactoryXmlObj {public static BuildXmlObj getBuildXmlObj(String name){BuildXmlObj bxj = null;switch(name){case "Student" :bxj = new BuildXmlStudent();break;//..}return bxj;}}7.4 客戶段代碼:
SAXBuilder sbd = new SAXBuilder();Document docJdom = null;File xmlfile = null;try{xmlfile = new File("/home/gateman/Studies/JavaDesignPattern/xml/04.xml");docJdom = sbd.build(xmlfile);}catch(Exception e){e.printStackTrace();}Element rootElement = docJdom.getRootElement();//all the element named "id" & without namespaceIteratorIterable<Element> elist = rootElement.getDescendants(new ElementFilter("id"));Iterator it = elist.iterator();Element eId2 = null;//loopwhile(it.hasNext()){Element em = (Element)(it.next());if (2 == new Integer(em.getValue()).intValue()){eId2 = em.getParentElement(); break;}}//packaging to a Student objStudent std = null;BuildXmlObj bxj = FactoryXmlObj.getBuildXmlObj("Student");std = (Student)(bxj.buildObj(eId2));System.out.println(std);
1g
總結(jié)
以上是生活随笔為你收集整理的Java xml 工具 JDOM 使用详解.的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java 利用反射实现C#的委托
- 下一篇: Java TCP 编程简介