可扩展标记语言——eXtensible MarkupLanguage
用户可以自己定义语言标记,只要有开始和闭合标签即可。
xsl装饰、修饰xml的显示结果。
dtd约束xml文件中的标记。
Ø XML的优点:
1、xml可以让数据和标记分离。
2、异质信息互通
3、机器语言
4、用交流语言替代html装饰语言
5、简单易用
6、可以自定义、可扩展
Ø XML和HTML比较
<table border="1" cellpadding="0" cellspacing="0" width="604"><tbody><tr><td valign="top" width="189"><p><span style="font-size:13px">比较内容</span></p></td><td valign="top" width="189"><p><span style="font-size:13px">HTML</span></p></td><td valign="top" width="224"><p><span style="font-size:13px">XML</span></p></td></tr><tr><td valign="top" width="189"><p><span style="font-size:13px">可扩展性</span></p></td><td valign="top" width="189"><p><span style="font-size:13px">不具有扩展性、标记固定</span></p></td><td valign="top" width="224"><p><span style="font-size:13px">是元标记语言,可以定义新标记,用户可以自定义标记</span></p></td></tr><tr><td valign="top" width="189"><p><span style="font-size:13px">侧重点</span></p></td><td valign="top" width="189"><p><span style="font-size:13px">侧重于信息的表现形式为什么格式被关注</span></p></td><td valign="top" width="224"><p><span style="font-size:13px">侧重于结构化的描述信息,数据是什么为XML所关注</span></p></td></tr><tr><td valign="top" width="189"><p><span style="font-size:13px">语法</span></p></td><td valign="top" width="189"><p><span style="font-size:13px">不严格(嵌套、配对)</span></p></td><td valign="top" width="224"><p><span style="font-size:13px">严格嵌套、配对,并按照DTD要求输出</span></p></td></tr><tr><td valign="top" width="189"><p><span style="font-size:13px">可读性、可维护性</span></p></td><td valign="top" width="189"><p><span style="font-size:13px">难于阅读和维护</span></p></td><td valign="top" width="224"><p><span style="font-size:13px">结构清晰,便于阅读维护</span></p></td></tr><tr><td valign="top" width="189"><p><span style="font-size:13px">数据本身、显示</span></p></td><td valign="top" width="189"><p><span style="font-size:13px">数据和显示合为一处</span></p></td><td valign="top" width="224"><p><span style="font-size:13px">数据与显示分离</span></p></td></tr><tr><td valign="top" width="189"><p><span style="font-size:13px">重用性</span></p></td><td valign="top" width="189"><p><span style="font-size:13px">低</span></p></td><td valign="top" width="224"><p><span style="font-size:13px">可重用性高</span></p></td></tr></tbody></table>
Ø **JDOM操作XML**
JDOM可以很方便的操作XML文档,完成XML内容的创建、修改,已经遍历Document文档中的XML元素,完成查询等。下面我们就用JDOM完成这些功能。
# 准备
首先我们要准备jdom相关的jar包
jdom-jar下载地址:[http://www.jdom.org/dist/binary/](http://www.jdom.org/dist/binary/)
jaxen在jdom的zip压缩包中可以找到。
Junit是测试用的,可以不添加。但需要用main方法测试。
Junit-jar[http://ebr.springsource.com/repository/app/bundle/version/download?name=com.springsource.org.junit&version=4.8.1&type=binary](http://ebr.springsource.com/repository/app/bundle/version/download?name=com.springsource.org.junit&version=4.8.1&type=binary)
其次,是准备测试工作。部分测试代码:
~~~
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文档,添加一个元素root
doc = 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,值为110
Element 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元素,文本内容是green
carEl.addContent(new Element("color").setText("green"));
//创建一个license的元素
Element licenseEl = new Element("license");
//为license元素添加文本内容
licenseEl.addContent("1ABC234");
//创建一个state的属性,值为CA
licenseEl.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();
}
}
~~~