dom4j使用大全
dom4j是dom4j.org出品的一个开源XML解析包,它的网站中这样定义:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- interface java.lang.Cloneable
- interface org.dom4j.Node
- interface org.dom4j.Attribute
- interface org.dom4j.Branch
- interface org.dom4j.CharacterData
- interface org.dom4j.DocumentType
- interface org.dom4j.Entity
- interface org.dom4j.ProcessingInstruction
// 从文件读取XML,输入文件名,返回XML文档
public Document read(String fileName) throws formedURLException, DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File(fileName));
return document;
}
其中,reader的read方法是重载的,可以从InputStream, File, Url等多种不同的源来读取。得到的Document对象就带表了整个XML。根据本人自己的经验,读取的字符编码是按照XML文件头定义的编码来转换。如果遇到乱码问题,注意要把各处的编码名称保持一致即可。
(2) 取得Root节点
读取后的第二步,就是得到Root节点。熟悉XML的人都知道,一切XML分析都是从Root元素开始的。
public Element getRootElement(Document doc){
return doc.getRootElement();
}
(3)遍历XML树
DOM4J提供至少3种遍历节点的方法:
1) 枚举(Iterator)
// 枚举所有子节点
for ( Iterator i = root.elementIterator(); i.hasNext(); ) {
Element element = (Element) i.next();
}
// 枚举名称为foo的节点
for ( Iterator i = root.elementIterator(foo); i.hasNext();) {
Element foo = (Element) i.next();
}
// 枚举属性
for ( Iterator i = root.attributeIterator(); i.hasNext(); ) {
Attribute attribute = (Attribute) i.next();
}
2)递归
递归也可以采用Iterator作为枚举手段,但文档中提供了另外的做法
public void treeWalk() {
treeWalk(getRootElement());
}
public void treeWalk(Element element) {
for (int i = 0, size = element.nodeCount(); i < size; i++) {
Node node = element.node(i);
if (node instanceof Element) {
treeWalk((Element) node);
} else { // do something....
}
}
}
3)
Visitor模式
最令人兴奋的是DOM4J对Visitor的支持,这样可以大大缩减代码量,并且清楚易懂。了解设计模式的人都知道,Visitor是GOF (四人组)设计模式之一。其主要原理就是两种类互相保有对方的引用,并且一种作为Visitor去访问许多Visitable。我们来看DOM4J中的Visitor模式(快速文档中没有提供)只需要自定一个类实现Visitor接口即可。
public class MyVisitor extends VisitorSupport {
public void visit(Element element){
System.out.println(element.getName());
}
public void visit(Attribute attr){
System.out.println(attr.getName());
}
}
调用: root.accept(new MyVisitor())
Visitor接口提供多种Visit()的重载,根据XML不同的对象,将采用不同的方式来访问。上面是给出的Element和Attribute的简单实现,一般比较常用的就是这两个。VisitorSupport是DOM4J提供的默认适配器,Visitor接口的Default Adapter模式,这个模式给出了各种visit(*)的空实现,以便简化代码。
注意,这个Visitor是自动遍历所有子节点的。如果是root.accept(MyVisitor),将遍历子节点。我第一次用的时候,认为是需要自己遍历,便在递归中调用Visitor,结果可想而知。
4) XPath支持
DOM4J对XPath有良好的支持,如访问一个节点,可直接用XPath选择。
public void bar(Document document) {
List list = document.selectNodes( //foo/bar );
Node node = document.selectSingleNode(//foo/bar/author);
String name = node.valueOf( @name );
}
(4)创建XML
一般创建XML是写文件前的工作,这就像StringBuffer一样容易。
public Document createDocument() {
Document document = DocumentHelper.createDocument();
Element root = document.addElement(root);
Element author1 =
root
.addElement(author)
.addAttribute(name, James)
.addAttribute(location, UK)
.addText(James Strachan);
Element author2 =
root
.addElement(author)
.addAttribute(name, Bob)
.addAttribute(location, US)
.addText(Bob McWhirter);
return document;
}
(5) 更新XML文档(包括增加、删除、修改)
具体的参见下面详细的代码
(6)字符串与XML的转换
有时候经常要用到字符串转换为XML或反之,
// XML转字符串
Document document = ……;
String text = document.asXML();
// 字符串转XML
String text = <person> <name>James</name> </person>;
Document document = DocumentHelper.parseText(text);
(7)文件输出
一个简单的输出方法是将一个Document或任何的Node通过write方法输出
FileWriter out = new FileWriter( book.xml );
document.write(out);
如果你想改变输出的格式,比如美化输出或缩减格式,可以用XMLWriter类
public void write(Document document) throws IOException {
// 指定文件
XMLWriter writer = new XMLWriter( new FileWriter( book.xml ));
writer.write( document );
writer.close();
// 美化格式,生成的xml文件是排版对称的文件
OutputFormat format = OutputFormat.createPrettyPrint();
writer = new XMLWriter( System.out, format );
writer.write( document );
// 缩减格式,生成的xml文件以一行显示
format = OutputFormat.createCompactFormat();
writer = new XMLWriter( System.out, format );
writer.write( document );
(8)中文编码问题
如果你在生成的xml文件中包含有中文信息的话,如果不进行处理就会出现以下的异常:
(1)org.dom4j.DocumentException: Invalid byte 2 of 2-byte UTF-8 sequence. Nested exception: Invalid byte 2 of 2-byte UTF-8 sequence。
(2)com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 2 of 2-byte UTF-8 sequence。
……
此时的解决办法就是:
OutputFormat opf=OutputFormat.createPrettyPrint(); //美化格式
opf.setEncoding("gb2312"); //解决中文乱码问题,在生成的xml文档声明中可以看到设置的编码。
XMLWriter writer = new XMLWriter(new FileWriter(new File(filename)),opf); // 将document中的内容写入文件中
我将以上的内容要点综合后具体实现了一下。
具体的代码如下(已经通过测试):
package com.bjsxt.dom4j;
import java.io.File;
import java.io.FileWriter;
import java.util.Iterator;
import java.util.List;
import java.io.IOException;
import org.apache.derby.vti.IFastPath;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.dom4j.io.OutputFormat;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import org.dom4j.VisitorSupport;
public class DOM4JTest {
public static void main(String[] args) throws Exception {
createXMLFile("book.xml");
analysisXMLFile("book.xml");
modiXMLFile("book.xml");
}
//创建xml文档
public static int
createXMLFile(String filename){
int returnValue = 0; // 返回操作结果, 0表失败, 1表成功
// 建立document对象
Document document = DocumentHelper.createDocument();
//建立XML文档的根books
Element booksElement = document.addElement("books");
// 加入一行注释
booksElement.addComment("This is a test for dom4j, holen, 2012.5.11");
// 加入第一个book节点
Element bookElement = booksElement.addElement("book");
//加入show属性内容
bookElement.addAttribute("show","yes");
//加入title节点
Element titleElement = bookElement.addElement("title");
//为title设置内容
titleElement.setText("Dom4j Tutorials");
//类似的完成后两个book
bookElement = booksElement.addElement("book");
bookElement.addAttribute("show","yes");
bookElement.setText("三国演义");
titleElement = bookElement.addElement("title");
titleElement.setText("Lucene Studing");
bookElement = booksElement.addElement("book");
bookElement.addAttribute("show","no");
titleElement = bookElement.addElement("title");
titleElement.setText("Lucene in Action");//加入owner节点
Element ownerElement = booksElement.addElement("owner");
ownerElement.setText("O'Reilly");
try{
//美化格式
OutputFormat opf=OutputFormat.createPrettyPrint();//解决中文乱码问题,在生成的xml文档声明中可以看到设置的编码。
opf.setEncoding("gb2312");
// 将document中的内容写入文件中
XMLWriter writer = new XMLWriter(new FileWriter(new File(filename)),opf);
writer.write(document);
writer.close();
returnValue = 1; //执行成功,需返回1
}catch(Exception ex){
ex.printStackTrace();
}
return returnValue;
}
//解析xml文档(查询某个节点和遍历整个文档),提供四种方式:public static void
analysisXMLFile(String filename){
(1)枚举方式(iterator)遍历XML文档,推荐
iteratorTraversal(filename) ;
(2)递归函数遍历xml文档,不推荐。
// SAXReader sr = new SAXReader();
// try {
// Document document = sr.read(filename);
// Element rootElement = document.getRootElement();
// RecursiveTraversal(rootElement);
// }catch (Exception e) {
// e.printStackTrace();
// }
(3)visitor观察者的方式遍历xml文档 ,推荐。
// SAXReader sr = new SAXReader();
// try {
// Document document = sr.read(filename);
// Element rootElement = document.getRootElement();
// rootElement.accept(new MyVisitor());
// }catch (Exception e) {
// e.printStackTrace();
// }
(4)xpath方式遍历xml文件,此种方式需导入jaxen-1.1-bete-X.jar,推荐。
// xpathTraversal(filename) ;
}
//枚举方式遍历XML文档所有节点,推荐使用
public static void iteratorTraversal(String filename) {
SAXReader sr = new SAXReader();
try {
Document document = sr.read(filename);
Element rootElement = document.getRootElement();
Iterator k1 =rootElement.elementIterator("book"); //枚举该节点的所有book子节点
if (!k1.hasNext()) {
System.out.println("不能枚举该节点的孙节点");
}else{
while (k1.hasNext()) {
Element titleElement = (Element) k1.next();
String str = titleElement.getText();
System.out.println(str);
}
}
Iterator k2 =rootElement.elementIterator("title"); //不能枚举title这个孙节点
Iterator k3 =rootElement.attributeIterator(); //枚举该节点的所有的属性
System.out.println("根元素"+rootElement.getName()+"的子、孙元素为:");
Iterator i = rootElement.elementIterator(); //枚举该节点的所有子节点
while(i.hasNext())
{
System.out.println("===================");
Element element = (Element) i.next();
System.out.println("子节点 "+element.getName());
for ( Iterator j = element.attributeIterator(); j.hasNext(); ) {
Attribute attribute = (Attribute) j.next();
System.out.print(" 属性:"+attribute.getName());
System.out.print( "=") ;
System.out.print(attribute.getValue());
System.out.println(); //目的是与下面循环的子节点分行显示
}
for ( Iterator j = element.elementIterator(); j.hasNext(); ) {
Element element1 = (Element) j.next();
System.out.print(" 孙节点:"+element1.getName());
System.out.println("");
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
//递归方式遍历XML文档所有节点,不推荐使用
public static void RecursiveTraversal(Element element) {
int size = element.nodeCount(); //统计该元素的子孙节点个数
for (int i = 0 ; i < size; i++){
Node node = element.node(i); //该元素的第i个子孙节点
System.out.println("节点:"+node.getName());
if (node instanceof Element) {
RecursiveTraversal((Element) node);
} else {
continue;
}
}
}
//xpath方式遍历XML文档所有节点,推荐使用
public static void xpathTraversal(String filename){
SAXReader sr = new SAXReader();
try {
Document document = sr.read(filename);
List<Node> list = document.selectNodes( "/books/book");
for(Node n : list) {
System.out.println(n.getName());
System.out.println(n.valueOf("@show")); //取show属性的值
if (n.getText()!=null) {
System.out.println(n.getText()); //取book元素的文本值
}else{
//continue;
}
}
Node node = document.selectSingleNode("books/book" );
System.out.println(node.getName());
System.out.println(node.valueOf("@show"));
if (node.getText()!=null) {
System.out.println(node.getText()); //取book元素的文本值
}else{
//continue;
}
} catch (Exception e) {
e.printStackTrace();
}
}
//更新xml文件 (增加、刪除、修改)
public static int modiXMLFile(String filename){
int returnValue = 0;
try {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File(filename));
//更新内容之一: 如果book节点中show属性的内容为yes,则修改成no
//先用xpath查找属性
List<Node> list = document.selectNodes("/books/book/@show" );
Iterator iter = list.iterator();
while(iter.hasNext()){
Attribute attribute = (Attribute)iter.next();
if(attribute.getValue().equals("yes")){
attribute.setValue("no"); //修改属性值用的是setValue()
}
}
//更新内容之二: 把owner项内容改为刘德华,并在owner节点中加入date节点,
//date节点的内容为2012-05-11,还为date节点添加一个属性type
//先用xpath查找元素
list = document.selectNodes("/books/owner" );
iter = list.iterator();
if(iter.hasNext()){
Element ownerElement = (Element)iter.next();
ownerElement.setText("刘德华"); //修改文本值用的是setText()
Element dateElement = ownerElement.addElement("date");
dateElement.setText("2012-05-11");
dateElement.addAttribute("type","Gregorian calendar");
}
//更新内容之三: 若title内容为Dom4j Tutorials,则删除该节点
//先用xpath查找元素
list = document.selectNodes("/books/book");
iter = list.iterator();
while(iter.hasNext()){
Element bookElement = (Element)iter.next();
Iterator iterator = bookElement.elementIterator("title"); //与document对象不同的
while(iterator.hasNext()){
Element titleElement=(Element)iterator.next();
if(titleElement.getText().equals("Dom4j Tutorials")){
bookElement.remove(titleElement); //必须是该节点的父节点删除它
}
}
}
try{
//美化格式,此时的xml文档多行书写OutputFormat opf=OutputFormat.createPrettyPrint();
//解决中文乱码问题,在生成的xml文档声明中可以看到设置的编码。
opf.setEncoding("gbk");//将document中的内容写入文件中
XMLWriter writer = new XMLWriter(new FileWriter(filename),opf);
writer.write(document);
writer.close();
returnValue = 1; //执行成功,需返回1
}catch(Exception ex){
ex.printStackTrace();
}
}catch(Exception ex){
ex.printStackTrace();
}
return returnValue;
}
}
//Visitor模式 提供了很好的遍历XML文档的方式
class MyVisitor extends VisitorSupport {
private String name;
public void visit(Element element){
name=element.getName();
System.out.println(" 元素<"+name+">");
}
public void visit(Attribute attr){
System.out.println(" 元素"+name+"的属性有"+attr.getName());
}
public void visit(Text text) {
System.out.println(text.getText());
}
}