JDOM 操作XML
可扩展标记语言——eXtensible Markup Language
用户可以自己定义语言标记,只要有开始和闭合标签即可。
xsl装饰、修饰xml的显示结果。
dtd约束xml文件中的标记。
Ø XML的优点:
1、xml可以让数据和标记分离。
2、异质信息互通
3、机器语言
4、用交流语言替代html装饰语言
5、简单易用
6、可以自定义、可扩展
Ø XML和HTML比较
|   比较内容  |    HTML  |    XML  | 
|   可扩展性  |    不具有扩展性、标记固定  |    是元标记语言,可以定义新标记,用户可以自定义标记  | 
|   侧重点  |    侧重于信息的表现形式为什么格式被关注  |    侧重于结构化的描述信息,数据是什么为XML所关注  | 
|   语法  |    不严格(嵌套、配对)  |    严格嵌套、配对,并按照DTD要求输出  | 
|   可读性、可维护性  |    难于阅读和维护  |    结构清晰,便于阅读维护  | 
|   数据本身、显示  |    数据和显示合为一处  |    数据与显示分离  | 
|   重用性  |    低  |    可重用性高  | 
Ø JDOM操作XML
JDOM可以很方便的操作XML文档,完成XML内容的创建、修改,已经遍历Document文档中的XML元素,完成查询等。下面我们就用JDOM完成这些功能。
# 准备
首先我们要准备jdom相关的jar包
jdom-jar下载地址:http://www.jdom.org/dist/binary/
jaxen在jdom的zip压缩包中可以找到。
Junit是测试用的,可以不添加。但需要用main方法测试。
其次,是准备测试工作。部分测试代码:
package com.hoo.test;import java.io.File;import java.io.IOException;import java.util.List;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import org.jdom.Attribute;import org.jdom.Comment;import org.jdom.Document;import org.jdom.Element;import org.jdom.JDOMException;import org.jdom.Namespace;import org.jdom.Text;import org.jdom.input.SAXBuilder;import org.jdom.output.XMLOutputter;import org.jdom.xpath.XPath;import org.junit.After;import org.junit.Before;import org.junit.Test;/** * <b>function:</b> JDOM操作XML * @author hoojo * @createDate 2011-8-4 下午12:34:09 * @file DocumentTest.java * @package com.hoo.test * @project JDOMTest * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */public class DocumentTest {
    private XMLOutputter out = null;@Before
public void init() {
        //输出文件信息        out = new XMLOutputter();}
@After
public void destory() {
        if (out != null) {out = null;
}
System.gc();
}
    /**     * <b>function:</b>输出Document文档信息     * @author hoojo     * @createDate 2011-8-5 下午12:10:27     * @param doc     */private void print(Document doc) {
        //设置XML文件编码格式        //out.setFormat(Format.getCompactFormat().setEncoding("gb2132"));System.out.println(out.outputString(doc));
}
private void fail(Object o) {
        if (o != null) {System.out.println(o);
}
}
}
# 创建Document
/** * 创建xml元素 */@Test
public void createDoc() {
Document doc = null;
    //method 1、创建一个Doc文档,添加一个元素rootdoc = new Document(new Element("root"));
print(doc);
    //method 2、创建一个Doc文档,添加一个元素root,设置root元素的节点文本doc = new Document(new Element("root").setText("this is a root el"));
print(doc);
    //method 3、创建一个Doc文档,添加一个元素root,设置root元素的节点文本且添加一个属性id,值为110Element root = new Element("root");
    root.setText("this is a root el");root.setAttribute("id", "110");
doc.setRootElement(root);
    fail("method 3: \n" + out.outputString(doc));    //method 4、创建一个Doc文档,添加一个元素root,设置root元素的节点文本    doc = new Document();doc.addContent(new Element("root").setText("this is a root el"));
    fail("method 4: \n" + out.outputString(doc));fail(doc.toString());
}
* new Document可以创建一个doc文档
当给Document传递一个Element参数时,这个Element就是根元素;
当调用Document的setRootElement方法时,可以设置当前Doc的根元素;
当调用doc的addContent的时候,添加的元素将会是根元素;
doc = new Document(new Element("root").setText("this is a root el"));
上面就创建了一个doc,根元素是root,root节点的内容是this is a root el;
注意setText方法返回的对象是当前Element,类似是StringBuffer的append方法;
* new Element()可以创建一个元素
如果传递参数那么这个参数将会是元素节点的名称;
Element的setText方法可以设置元素的文本值;
Element root = new Element("root");
root.setText("this is a root el");
创建一个节点名称为root的元素,文本是this is a root el
* setAttribute()可以设置某个具体节点的属性值
root.setAttribute("id", "110");
给root节点添加一个id,值为110
* addContent添加注释
root .addContent(new Comment("注释"));
在root元素下添加一个注释;
addContent是向元素中添加内容,而setContent是设置内容;
* setText设置元素文本内容
root.setText("this is a root el");
同样
root. setContent(new Text("this is text"))
同样
root.addContent("this is text");
下面用上面的这些方法,创建一篇XML文档。文档内容如下:
/**    创建一遍xml文档    <?xml version="1.0" encoding="UTF-8"?>    <car vin="123fhg5869705iop90">      <!--Description of a car-->      <make>Toyota</make>      <model>Celica</model>      <year>1997</year>      <color>green</color>      <license state="CA">1ABC234</license>    </car> */@Test
public void createXMLDoc() {
    //创建一个car的元素Element carEl = new Element("car");
    //创建vin属性,并设置值carEl.setAttribute("vin", "123fhg5869705iop90");
    //创建注释carEl.addContent(new Comment("Description of a car"));
    //创建一个make元素,设置文本内容carEl.addContent(new Element("make").setText("Toyota"));
    //创建一个model元素,添加一个文本元素carEl.addContent(new Element("model").setContent(new Text("Celica")));
    //创建一个year元素,添加文本内容carEl.addContent(new Element("year").addContent("1997"));
    //创建一个color元素,文本内容是greencarEl.addContent(new Element("color").setText("green"));
    //创建一个license的元素Element licenseEl = new Element("license");
    //为license元素添加文本内容    licenseEl.addContent("1ABC234");    //创建一个state的属性,值为CAlicenseEl.setAttribute("state", "CA");
    //将licenseEl添加到根元素中carEl.addContent(licenseEl);
    //将car元素设置为根元素    Document doc = new Document(carEl);print(doc);
    /*out = new XMLOutputter();    try {        out.output(doc, System.out);    } catch (IOException e) {        e.printStackTrace();    }*/}
方法运行后,所创建的文档和上面注释文档内容相同
# 读取XML文件的内容
disk.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<HD>
<disk name="C">
<capacity>8G</capacity>
<directories>200</directories>
<files>1580</files>
</disk>
<disk name="D">
<capacity>10G</capacity>
<directories>500</directories>
<files>3000</files>
</disk>
<disk2 name="E">
<capacity>11G</capacity>
<directories>50</directories>
<files size="200" modifyDate="2011-08-3">
<file>Java book</file>
<file>Spring.txt</file>
<file>strtus.doc</file>
</files>
</disk2>
<files size="220">500</files>
</HD>
读取disk文件的内容,代码如下:
/** * <b>function:</b>读取xml文件中的元素 * @author hoojo * @createDate 2011-8-4 下午04:54:17 */@Test
@SuppressWarnings("unchecked")public void readXMLContent() {
    SAXBuilder builder = new SAXBuilder();    try {Document doc = builder.build(new File("file/disk.xml"));
Element rootEl = doc.getRootElement();
        //获得所有子元素List<Element> list = rootEl.getChildren();
        //List<Element> list = rootEl.getChildren("disk");        for (Element el : list) {            //获取name属性值            String name = el.getAttributeValue("name");            //获取子元素capacity文本值            String capacity = el.getChildText("capacity");            //获取子元素directories文本值            String directories = el.getChildText("directories");            String files = el.getChildText("files");            System.out.println("磁盘信息:");            System.out.println("分区盘符:" + name);            System.out.println("分区容量:" + capacity);            System.out.println("目录数:" + directories);            System.out.println("文件数:" + files);            System.out.println("-----------------------------------");}
    } catch (JDOMException e) {e.printStackTrace();
    } catch (IOException e) {e.printStackTrace();
}
}
* getChildren方法可以获取所有子元素
* getChildren(elName)可以获取所有名称为elName的子节点
* getAttributeValue可以获取指定属性的值
* getChildText可以获取子节点的文本值
# 递归显示文档信息
/** * 递归返回指定格式的“--” */private String format(int i) {
    String temp = "";    if (i > 0) {        temp += "--";i--;
temp += format(i);
}
    return temp;}
/** * <b>function:</b>显示当前节点所有Element的属性信息 * @author hoojo * @createDate 2011-8-4 下午06:10:53 * @param el * @return */@SuppressWarnings("unchecked")private String getAttrInfo(Element el) {List<Attribute> attrs = el.getAttributes();
    return getAttrInfo(attrs);}
/** * <b>function:</b>显示属性信息 * @author hoojo * @createDate 2011-8-9 下午03:52:59 * @param attrs * @return */private String getAttrInfo(List<Attribute> attrs) {    StringBuilder info = new StringBuilder();    for (Attribute attr : attrs) {info.append(attr.getName()).append("=").append(attr.getValue()).append(", ");
}
    if (info.length() > 0) {return "[" + info.substring(0, info.length() - 2)+ "]";
}
return "";
}
/** * <b>function:</b>递归显示文档节点元素信息 * @author hoojo * @createDate 2011-8-4 下午05:56:34 * @param i * @param list */@SuppressWarnings("unchecked")private void print(int i, List<Element> list) {
i++;
    for (Element el : list) {List<Element> childs = el.getChildren();
        if (childs.size() > 0) {            fail(format(i) + el.getName() + "  " + getAttrInfo(el));print(i, childs);
        } else {fail(format(i) + el.getName() + ":" + el.getText() + " " + getAttrInfo(el));
}
}
}
调用print(0, root.getChildren());方法就可以看到一篇格式化后输出的文档内容
#############显示文档信息###############
--HD
----disk [name=C]
------capacity:8G
------directories:200
------files:1580
----disk [name=D]
------capacity:10G
------directories:500
------files:3000
----disk2 [name=E]
------capacity:11G
------directories:50
------files [size=200, modifyDate=2011-08-3]
--------file:Java book
--------file:Spring.txt
--------file:strtus.doc
----files:500 [size=220]
# XPath查询遍历XML文档
/** * <b>function:</b>用xpath遍历xml信息 * @author hoojo * @createDate 2011-8-4 下午04:56:52* xpath参考:http://www.w3school.com.cn/xpath/xpath_functions.asp *  *     nodeName 选取此节点的所有子节点     /    从根节点选取     //   从匹配选择的当前节点选择文档中的节点,而不考虑它们的     .    选取当前节点     ..   选取当前节点的父节点     @  选取属性         * 匹配任何元素节点     @* 匹配任何属性节点     node() 配任何类型的节点         ancestor 选取当前节点的所有先辈(父、祖父等)     ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身     attribute 选取当前节点的所有属性     child 选取当前节点的所有子元素。     descendant 选取当前节点的所有后代元素(子、孙等)。     descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。     following 选取文档中当前节点的结束标签之后的所有节点。     namespace 选取当前节点的所有命名空间节点     parent 选取当前节点的父节点。     preceding 选取文档中当前节点的开始标签之前的所有节点。     preceding-sibling 选取当前节点之前的所有同级节点。     self 选取当前节点。        child::book 选取所有属于当前节点的子元素的 book 节点     attribute::languane 选取当前节点的 languange 属性     child::* 选取当前节点的所有子元素     attribute::* 选取当前节点的所有属性     child::text() 选取当前节点的所有文本子节点     child::node() 选取当前节点的所有子节点     descendant::book 选取当前节点的所有 book 后代     ancestor::book 选择当前节点的所有 book 先辈 */@SuppressWarnings("unchecked")@Test
public void queryElementByXPath() {
    SAXBuilder builder = new SAXBuilder();    try {Document doc = builder.build(new File("file/disk.xml"));
        List<Element> list = XPath.selectNodes(doc, "/HD/disk");        for (Element el : list) {            String name = el.getAttributeValue("name");            String capacity = el.getChildText("capacity");            String directories = el.getChildText("directories");            String files = el.getChildText("files");            System.out.println("磁盘信息:");            System.out.println("分区盘符:" + name);            System.out.println("分区容量:" + capacity);            System.out.println("目录数:" + directories);            System.out.print("文件数:" + files);String capacityText = ((Text) XPath.selectSingleNode(el, "//disk[@name='" + name + "']/capacity/text()")).getTextNormalize();
            System.out.println("#" + capacityText);            System.out.println("-----------------------------------");}
        //显示文档信息        System.out.println("#############显示文档信息###############");print(0, doc.getContent());
        //获得hd元素        System.out.println("#############显示HD子元素信息###############");        Element root = (Element) XPath.selectSingleNode(doc, "/HD");        //fail(root.getChildren().size());print(0, root.getChildren());
        //获取hd下所有元素        System.out.println("#############显示HD子元素信息###############");        List roots = (List) XPath.selectNodes(doc, "/HD/*");        //fail(roots.size());print(0, roots);
        //获得hd下的所有disk元素        System.out.println("#############显示disk信息###############");        roots = (List) XPath.selectNodes(doc, "/HD/disk");        //fail(roots.size());print(0, roots);
        System.out.println("#############显示disk2信息###############");        roots = (List) XPath.selectNodes(doc, "/HD/disk2");print(0, roots);
        System.out.println("#############显示任意路径下的files信息###############");        roots = (List) XPath.selectNodes(doc, "//files");print(0, roots);
        System.out.println("#############显示任意路径下的files指定下标的file信息###############");        roots = (List) XPath.selectNodes(doc, "//files/file[1]");print(0, roots);
        System.out.println("#############显示任意路径下的files最后的file信息###############");        roots = (List) XPath.selectNodes(doc, "//files/file[last()]");print(0, roots);
        System.out.println("#############显示任意路径下的files倒数第二的file信息###############");        roots = (List) XPath.selectNodes(doc, "//files/file[last() - 1]");print(0, roots);
        System.out.println("#############显示任意路径下的files的子元素file位置position在第二的file信息###############");        roots = (List) XPath.selectNodes(doc, "//files/file[position() = 2]");        //roots = (List) XPath.selectNodes(doc, "//files/file[position() > 2]");print(0, roots);
        System.out.println("#############显示任意路径下的files第三个file的当前节点的前面所有同级节点信息###############");        roots = (List) XPath.selectNodes(doc, "//files/file[3]/preceding-sibling::*");print(0, roots);
        System.out.println("#############显示任意路径下的disk2之前的所有节点信息###############");        roots = (List) XPath.selectNodes(doc, "//disk2/preceding::*");print(0, roots);
        System.out.println("#############显示任意路径下的disk2之后的所有节点信息###############");        roots = (List) XPath.selectNodes(doc, "//disk2/following::*");print(0, roots);
        System.out.println("#############显示任意路径下的files的所有属性信息###############");        roots = (List) XPath.selectNodes(doc, "//files/attribute::*");fail(getAttrInfo(roots));
        System.out.println("#############显示任意路径下的节点是disk属性name=C的信息###############");        roots = (List) XPath.selectNodes(doc, "//disk[@name='C']");print(0, roots);
        System.out.println("#############显示任意路径下的节点是disk的子元素的文本中含义5和8节点的信息###############");        roots = (List) XPath.selectNodes(doc, "//disk/child::*[contains(text(), '8') and contains(text(), '5')]");        //roots = (List) XPath.selectNodes(doc, "//disk/child::*[contains(text(), '8') or contains(text(), '5')]");print(0, roots);
        System.out.println("#############显示任意路径下的节点是files且有属性size的信息###############");        roots = (List) XPath.selectNodes(doc, "//files[@size]");print(0, roots);
        System.out.println("#############显示HD节点下capacity的值为11G的信息###############");        //roots = (List) XPath.selectNodes(doc, "/HD/disk/capacity[text()='11G']");        roots = (List) XPath.selectNodes(doc, "/HD/*/capacity[text()='11G']");        //roots = (List) XPath.selectNodes(doc, "/*/*/capacity[text()='11G']");print(0, roots);
        //parent::*表示父节点集合        System.out.println("#############显示任意路径下的节点是files且属性size有值的父节点的信息###############");        roots = (List) XPath.selectNodes(doc, "//files[@size='200']/parent::*");print(0, roots);
        System.out.println("#############显示任意路径下的节点disk的子节点的capacity信息###############");        roots = (List) XPath.selectNodes(doc, "//disk/child::capacity");print(0, roots);
        //获取c盘的大小        System.out.println("获取c盘的大小");        Text filesText = (Text) XPath.selectSingleNode(doc, "/HD/disk[@name='C']/files/text()");System.out.println(filesText.getTextNormalize());
        //XPath function        /**        string concat (string, string, string*)  联接两个字符串         boolean starts-with (string, string)  判断某字符串是否以另一字符串开头         boolean contains (string, string)  判断某字符串是否包含另一字符串         string substring (string, number, number)  取子字符串         number string-length (string)  测字符串长度         number sum (node-set)  求和         number floor (number)  求小于此数的最大整数值         number ceiling (number)  求大于此数最小整数值        **/         System.out.println("获取@size的和大于200的");        roots = (List) XPath.selectNodes(doc, "//files[sum(@size) > 200]");print(0, roots);
        System.out.println("查找directories的内容长度小于3的");        roots = (List) XPath.selectNodes(doc, "//directories[string-length(text()) < 3]");print(0, roots);
        System.out.println("查找files的内容包含5的");        roots = (List) XPath.selectNodes(doc, "//files[contains(text(), '5')]");print(0, roots);
    } catch (JDOMException e) {e.printStackTrace();
    } catch (IOException e) {e.printStackTrace();
}
}
# 删除元素及其他操作
/** * <b>function:</b>打印doc相关信息 * @author hoojo * @createDate 2011-8-10 下午06:29:01 */@SuppressWarnings("unchecked")@Test
public void printInfo() {
    SAXBuilder builder = new SAXBuilder();    try {        //builder.setFeature("user", true);        //builder.setIgnoringBoundaryWhitespace(true);        //忽略元素内容的空格        //builder.setIgnoringElementContentWhitespace(true);Document doc = builder.build(new File("file/web.xml"));
        fail("baseURI: " + doc.getBaseURI());        fail("ContentSize: " + doc.getContentSize());        //System.out.println("getContent: ");        //print(0, doc.getContent());        fail("getContent index: " + doc.getRootElement().getContent(1));        fail("getDocType: " + doc.getDocType());        fail("getParent: " + doc.getRootElement().getContent(1).getParent());fail("getProperty: " + doc.getProperty("filter"));
        print(0, XPath.selectNodes(doc, "//*[contains(text(), '#')]"));fail("getText: " + ((Element)XPath.selectNodes(doc, "//*[contains(text(), '#')]").get(0)).getText());
fail("getTextTrim: " + ((Element)XPath.selectNodes(doc, "//*[contains(text(), '#')]").get(0)).getTextTrim());
fail("getTextNormalize: " + ((Element)XPath.selectNodes(doc, "//*[contains(text(), '#')]").get(0)).getTextNormalize());
        fail("hasRootElement: " + doc.hasRootElement());        //如果文档带有Namespace一定要设置Namespace,不然无法读取内容        Namespace ns = Namespace.getNamespace("http://java.sun.com/xml/ns/javaee");        Element servletEl = doc.getRootElement().getChild("servlet", ns);        fail("servletEl: " + servletEl);print(0, servletEl.getChildren());
fail("getChildText: " + servletEl.getChildText("servlet-class", ns));
fail("getChildTextNormalize: " + servletEl.getChildTextNormalize("servlet-name", ns));
fail("getChildTextTrim: " + servletEl.getChildTextTrim("servlet-class", ns));
        fail("getName: " + servletEl.getName());        fail("getNamespacePrefix: " + servletEl.getNamespacePrefix());        fail("getNamespace: " + servletEl.getNamespace());        fail("getQualifiedName: " + servletEl.getQualifiedName());        Element classEl = servletEl.getChild("servlet-class", ns);        fail("getText: " + classEl.getText());        fail("getTextNormalize: " + classEl.getTextNormalize());        fail("getTextTrim: " + classEl.getTextTrim());        fail("getValue: " + classEl.getValue());        //删除节点fail(doc.getRootElement().removeContent(3));
        //print(0, doc.removeContent());        //print(0, doc.getRootElement().getChildren());        fail(servletEl.removeChild("servlet-class", ns));        fail(servletEl.removeChildren("init-param", ns));print(0, servletEl.getChildren());
    } catch (JDOMException e) {e.printStackTrace();
    } catch (IOException e) {e.printStackTrace();
}
}