一、XML简介
1.1 概述
XML(ExtensibleMarkup language)在世界上的应用广泛性就如同眼镜在程序员中应用的广泛性一样,它与HTML一样,都出生于SGML,可用于简单的数据存储。对我们即将要学习如何在Java中使用它的同志们来说,有一个很好的消息要告诉大家:它很简单易学。
与数据库老大们(Acess,SQL,Oracle)不同,这些老大们提供了非常强大的数据分析、操作和存储能力,例如:索引,排序,增删改查,存储过程,游标,触发器等,XML能做到的,仅仅是展示和结构化数据信息而已,但是呢,“非常简单”使他与众不同。
在J2EE开发中,我们经常会看到他们那苗条的身影:启动Tomcat初始化配置文件的时候,以及著名的struts,hibernate,spring框架中,他们是如何解析这些配置文件又是如何把他们转化为对象随心所欲的使用的呢?下面,我们就掀开她们的盖头,欣赏一下她们的容颜。
1.2民间盛行的四种对XML的解析方式
Java生活中我们经常需要对XML进行解析以方便我们对数据进行操作,下面介绍在民间盛行的四种使用Java解析XML的方法,其中重点介绍DOM方式和DOM4J方式。
DOM(JAXP Crimson 解析器):W3C为HTML和XML分析器制定的标准接口规范,基于树,可随机动态访问和更新文档的内容、结构、样式。
SAX(simple API for XML):不是W3C的标准,而是由XML-DEV邮件列表成员于1998年为Java语言开发的一种基于事件的简单API。基于事件,逐行解析,顺序访问XML文档,速度快,处理功能简单。
JDOM:鉴于DOM的低效率,而SAX又不能随机处理XML文档,JasonHunter与Brett McLaughlin于2000年春天,开始创建一种能充分体现两者优势的API——JDOM(Java-based DOM,基于Java的DOM),它是一个基于Java的对象模型,树状结构,能使读取、操作和写入XML文档,比DOM更高效,比SAX更强大,但由于使用了大量的类而不使用接口导致灵活性降低。
DOM4J:DOM4J是一个易用的,开源的库,用于XML,XPath,XSLT。它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX,JAXP。它提供了大量的接口,因此比JDOM更具有灵活性。
二、预备知识
DOM(文档对象模型),W3C实现了DOM操作的规范化,并为多种语言提供了操作DOM的接口,如C,java,Javascript等等。
对象模型的构建是以面向对象的思想为基础,通过对问题进行抽象,构造出一组相关的模型,从而能够全面地捕捉问题空间的信息。而我们这里要介绍的文档对象模型通常是以树的形式对相关文档进行抽象,构建出由节点构成的文档树模型,从而实现更方便的对文档的各种操作。
节点:XML文档中的所有节点组成了一个文档树(或节点树)。XML文档中的每个元素、属性、文本等都代表着树中的一个节点。树起始于文档节点,并由此继续伸出枝条,直到处于这棵树最低级别的所有文本节点为止,常用节点类型如表2-1所示:
节点类型 | 附加说明 | 实例 |
元素节点(Element) | XML标记元素 | <company>…</company> |
属性节点(Attribute) | XML标记元素的属性 | name=”ntcsoft” |
文本节点(Text) | 包括在XML标记中的文本段 | 工程师A |
文档类型节点(DocumentType) | 文档类型声明 | ﹤!DOCTYPE…﹥ |
注释节点Comment | XmlComment类注释节点。 | <!—文档注释à |
表2-1
节点关系:下面我们从一个XML文档实例中了解一下各个节点之间的关系:
<company name="ntcsoft" > <department><employee position="developer">工程师A</employee></department> <department name="education"></department> </company> |
通过上面的XML文档,我们构建出如下树状文档对象模型,如图2-1所示:
图2-1
1.2 对DOM操作的各种招式
常用节点属性如表2-2所示:
属性 | 描述 |
nodeName | 结点名称 |
nodeValue | 结点内部值,通常只应用于文本结点 |
nodeType | 节点类型对应的数字 |
parentNode | 如果存在,指向当前结点的父亲结点 |
childNodes | 子结点列表 |
firstChild | 如果存在,指向当前元素的第一个子结点 |
lastChild | 如果存在,指向当前元素的最后一个子结点 |
previousSibling | 指向当前结点的前一个兄弟结点 |
nextSibling | 指向当前结点的后一个兄弟结点 |
attributes | 元素的属性列表 |
表 2-2
常用节点方法如表2-3所示:
操作类型 | 方法原型 | 描述 |
访问节点 | getElementById(id) | 根据ID属性查找元素节点 |
getElementsByName(name) | 根据name属性查找元素集 | |
getElementsByTagName(tagName) | 根据元素标记名称查找元素集 | |
创建节点 | createElement(tagName) | 创建元素节点 |
createTestNode(string) | 创建文本节点 | |
createAttribute(name) | 创建属性节点 | |
插入和添加节点 | appendChild(newChild) | 添加子节点到目标节点上 |
insertBefore(newChild,targetChild) | 将newChild节点插入到targetChild节点之前 | |
复制节点 | CloneNode(bool) | 复制该节点,由bool确定是否复制子节点 |
删除和替换节点 | removeChild(childName) | 删除由childName指定的节点 |
replaceChild(newChild,oldChild) | 用newChild替换oldChild | |
属性节点操作 | getAttribute(name) | 返回目标对象指定属性名称为name的属性值 |
setAttribute(name,value) | 修改目标节点指定属性名称为name的属性值为value | |
removeAttribute(name) | 删除目标节点指定属性名称为name的属性 |
表 2-3
三、实例解析XML
A. 实例环境:windows xp3,MyEclipse8.5,JDK1.6。
B. 环境搭建:
1. JDK的安装与配置可参考文档《JDK安装与环境变量配置》。
2. MyEclipse8.5的安装与使用请参考文档《Eclipse的安装与使用》。
3. 在MyEclipse下创建exercise工程,在src目录下创建文件名为Company.xml的XML文档,如图3-1:
图 3-1
Company.xml源码如下: <?xml version="1.0" encoding="UTF-8"?> <company name="ntcsoft" address="河南省郑州市"> <department deptNo="001" name="development"> <employee id="devHead" position="minister">许刚</employee> <employee position="developer">工程师A</employee> </department> <department deptNo="002" name="education"> <employee position="minister" telephone="1234567">申林</employee> <employee position="trainee">实习生A</employee> </department> </company>
|
4. 在工程下创建domain包,用于存放实体类,在此包下创建如下实体类,如图3-2:
图 3-2
Company.java主要源码如下: publicclass Company { private String name; private String address; private Department[] dept; …省略Seters and Getters…} } |
Department.java主要源码如下: publicclass Department { private String number; private String name; private Employee[] emp; …省略Seters and Getters…}
|
Employee.java主要源码如下: publicclass Employee { private String name; private String position; private String telephone; …省略Seters and Getters…}
|
5. 在工程下创建xmpParsers包,用于存放解析器,如图3-3所示:
图 3-3
6. 在工程下创建parsersTest包,用于存放解析器测试类,如图3-4所示:
图3-4
3.2 DOM方式解析XML
A. 概述:
DOM(Document Object Model文档对象模型),是W3C为HTML和XML分析器制定的标准接口规范。
B. 特点:
独立于语言,跨平台(可以在各种编程和脚本语言中使用),需要将整个文档读入内存,在内存中创建文档树,可随即访问文档中的特定节点,对内存的要求比较高,经过测试,访问速度相对于其他解析方式较慢,适用于简单文档的随即处理。
C. 在解析过程中需要使用的包、接口和类:
//为文档对象模型 (DOM) 提供接口 API(Java API for XML Processing (JAXP))。 import org.w3c.dom.*; //此类可以包含来自 XML 解析器或应用程序的基本错误和警告信息 import org.xml.sax.SAXException; //导入实体类 import domain.*; //标准IO,用于输入输出工作 import java.io.*; //用于从 XML 文档获取 DOM 文档实例并生成 DOM 对象树的解析器 import javax.xml.parsers.*; //用于创建将DOM文档树源对象转换为XML文档结果对象的转换器 import javax.xml.transform.*; //构造DOM文档树源对象 import javax.xml.transform.dom.DOMSource; //构造XML文档的结果对象 import javax.xml.transform.stream.StreamResult; |
D. 读取本地XML文档解析并转换为对象的步骤:
1. 创建XML文件解析器:我们可以通过javax.xml.parsers包下的工厂类DocumentBuilderFactory创建XML文档的解析器。
//创建DocumentBuilderFactory工厂实例。 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); //通过文档创建工厂创建文档创建器 DocumentBuilder dBuilder = dbfactory.newDocumentBuilder(); |
2. 解析XML文件并把该文件转换为文档对象:通过文档创建器DocumentBuilder的parse方法解析参数URL指定的XML文档,并返回一个Document 对象。
Document doc = dBuilder.parse(url); |
3. 操作该对象并获取数据。
E. 将对象转换为XML文件并写入本地的步骤:
1. 创建XML文档对象:
Document doc = dBuilder.newDocument(); //通过文档创建器创建一个新的文档对象。 |
2. 把实体对象转换为节点对象添加到创建好的XML文档对象中:
Element root = doc.createElement(“company”);//建立根节点company doc.appendChild(root); |
3. 使用javax.xml.transform包下TransformerFactory类创建文档转换器。
TransformerFactory tFactory = TransformerFactory.newInstance();//创建转换器工厂实例 Transformer tFormer= tFactory.newTransformer();//创建转换器 |
4. 通过文档转换器将XML文档的源对象转换为可写入本地的结果对象,并写入本地。
DOMSource dSource = new DOMSource(doc);//将DOM对象解析为转换器的输入源 //创建一个文件路径为filePath的文件对象,并将文件对象封装到结果集对象中 StreamResult sResult = new StreamResult(new File(filePath)); // 使用转换器将内存中的XML文档输入源转换为结果集输出到本地 tFormer.transform(dSource, sResult); |
F. 处理XML文档步骤:
1. 读取本地XML文档。
2. 通过节点的各种操作方法对节点进行增加,修改,删除等操作。
3. 重新将XML文档写入本地。
G. 解析器DOMParser.java的全部源码:
/** * Parse XML document with DOM. * Copyright 2010.9 NTCsoft(郑州蜂鸟科技有限公司) , Inc. All rights reserved. * @author ZhouYuan * @since 2010-9-24 * @version 1.0 */ //用于各种IO操作 import java.io.*; //用于从 XML 文档获取 DOM 文档实例并生成 DOM 对象树的解析器 import javax.xml.parsers.*; //用于创建将DOM文档树对象模型转换为XML文档模型的转换器 import javax.xml.transform.*; //构造DOM文档树源对象 import javax.xml.transform.dom.DOMSource; //构造XML文档的结果对象 import javax.xml.transform.stream.StreamResult; //为文档对象模型 (DOM) 提供接口API(Java API for XML Processing (JAXP)的组件)。 import org.w3c.dom.*; //此类可以包含来自 XML 解析器或应用程序的基本错误和警告信息 import org.xml.sax.SAXException; //导入实体类 import domain.*; /** * Parse XML document with DOM.*/ publicclass DOMParser { DocumentBuilderFactory dbfactory //创建DocumentBuilderFactory工厂实例。 = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder;
/** * 读XML文档: * 从本地读取XML文件,并将XML文档转换为Java对象。 * @param url 将要读取的XML文件的路径 * @return返回转换成功的Company对象*/ public Company readXML(String url){ //创建公司对象 Company comp = new Company();
try { dBuilder = dbfactory.newDocumentBuilder(); /* * Document 接口表示整个 HTML 或 XML 文档。 * parse方法:解析 URI 指定的 XML 文档,返回一个新的 DOM Document 对象。*/ Document doc = dBuilder.parse(url); //得到文档的根元素company Element root = doc.getDocumentElement(); //读取XML文档中的公司信息,并为公司对象设置相关属性。 comp.setAddress(root.getAttribute("address")); comp.setName(root.getAttribute("name")); //通过getElementsByTagName读取XML文档中的所有root节点下的department节点。 NodeList depts = root.getElementsByTagName("department"); //创建Department对象数组 Department[] deptList = new Department[depts.getLength()];
/* * 读取部门信息,并将部门对象添加至公司对象中。 */ for(int i = 0;i < depts.getLength();i++){ Department dept = new Department(); //使用NodeList的item()方法遍历节点,item(int index)方法可以返回集合中的第 index个项。 Element deptEmt = (Element) depts.item(i); dept.setName(deptEmt.getAttribute("name")); dept.setNumber(deptEmt.getAttribute("deptNo"));
//通过getElementsByTagName读取XML文档中的所有deptEmt节点下的employee节点 NodeList emps = deptEmt.getElementsByTagName("employee"); //创建Employee对象数组 Employee[] empList = new Employee[emps.getLength()];
/* * 读取雇员信息,并将雇员对象添加至部门对象中。*/ for(int j = 0;j < emps.getLength();j++){ Employee emp = new Employee(); //将XML中的employee节点转换为Employee对象emp。 Element empEmt = (Element) emps.item(j);
emp.setName(empEmt.getTextContent()); emp.setPosition(empEmt.getAttribute("position")); emp.setTelephone(empEmt.getAttribute("telephone")); //将emp对象添加至Employee对象数组empList中 empList[j]=emp; //将对象数组empList添加至部门对象dept的对象数组属性emp中。 dept.setEmp(empList); } //将dept对象添加至Department对象数组deptList中。 deptList[i]=dept; /* 将对象数组deptList添加至公司对象comp的对象数组属性deptList中, * 并返回该公司对象。*/ comp.setDept(deptList); } } catch (ParserConfigurationException e) { e.printStackTrace(); }catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
return comp; }
/** * 写XML文档: * 将java对象转换为XML文档,并将文档写入本地 * @param url 将要写入本地的XML文件的路径 * @param comp 需要转换的公司对象 * @return返回文档在本地的绝对路径*/ public String writeXML(String url,Company comp){ try { dBuilder = dbfactory.newDocumentBuilder(); //通过文档创建工厂创建文档创建器, Document doc = dBuilder.newDocument(); //并通过文档创建器创建一个新的文档对象。 Element root = doc.createElement("company");//建立根节点company //将根结点添加到文档中 doc.appendChild(root);
/* * 通过循环将公司对象中的部门对象数组转换成部门节点, * 并根据部门对象的属性设置部门节点的香瓜那属性, * 最后将该部门节点添加至相应的公司节点中 */ for(int i = 0;i < comp.getDept().length;i++){ Department dept = comp.getDept()[i]; Element deptEmt = doc.createElement("department"); deptEmt.setAttribute("deptNo", dept.getNumber()); deptEmt.setAttribute("name", dept.getName());
/* * 通过循环将部门对象中的员工对象数组转换成员工节点, * 并根据员工对象的属性设置员工节点的相关属性, * 最后将该员工节点添加至相应的部门节点中 */ for(int j = 0;j < dept.getEmp().length;j++){ Employee emp = dept.getEmp()[j]; Element empEmt = doc.createElement("employee"); empEmt.setAttribute("position", emp.getPosition()); empEmt.setAttribute("telphone", emp.getTelephone()); Text tNode = doc.createTextNode(emp.getName()); empEmt.appendChild(tNode); deptEmt.appendChild(empEmt); } root.appendChild(deptEmt); }
docToXML(doc, url);
} catch (ParserConfigurationException e) { e.printStackTrace(); } return url; }
/** * 将DOM树对象转换为可以写入本地的XML文档对象 * @param doc 需要转换的DOM对象 * @param filePath 转换后的XML文档存放的 * @return写入成功返回true,转换过程中出现异常返回false */ publicboolean docToXML(Document doc,String filePath){
//把XML文档输出到指定的文件 try { TransformerFactory tFactory = TransformerFactory.newInstance();//创建转换器工厂实例 Transformer tFormer= tFactory.newTransformer();//创建转换器 DOMSource dSource = new DOMSource(doc);//创建DOM输入源 StreamResult sResult = new StreamResult(new File(filePath));// 创建输出流
//设置写入XML文件的编码、空白符和版本 tFormer.setOutputProperty(OutputKeys.ENCODING,"UTF-8"); tFormer.setOutputProperty(OutputKeys.INDENT, "yes"); tFormer.setOutputProperty(OutputKeys.VERSION, "1.0");
tFormer.transform(dSource, sResult); // 将内存中的XML文档转换输出到本地
} catch (TransformerConfigurationException e) { e.printStackTrace(); returnfalse; }catch (TransformerException e) { e.printStackTrace(); returnfalse; } returntrue; }
/** * 处理XML文档: * 1.修改company节点的address属性为"郑州市金水区文化路与丰产路交叉口向东50米SOHO世纪城" * 2.修改工程师A的名字为王工程师 * 3.为"许刚"添加telephone属性,并将值设置为7654321 * 4.删除"申林"的telephone属性 * 5.删除名字为"实习生A"的employee节点 * @param inUrl 要处理的XML文档的url * @param outUrl 处理后的XML文档存放的的url */ publicvoid XMLHandle(String inUrl,String outUrl){ try { //获得XML文档的树对象 dBuilder = dbfactory.newDocumentBuilder(); dbfactory.setIgnoringElementContentWhitespace(true); Document doc = dBuilder.parse(inUrl); Element company = doc.getDocumentElement();//获得根节点 NodeList emps = company.getElementsByTagName("employee");//获得所有employee节点
//1.修改company节点的address属性 company.setAttribute("address", "郑州市金水区文产路交叉口向东50米SOHO世纪城");
//2.修改工程师A的名字为王工程师 //Element emp = (Element) company.getFirstChild().getLastChild(); Element emp = (Element) emps.item(1); emp.replaceChild(doc.createTextNode("王工程师"),emp.getFirstChild());
//3.为"许刚"添加telephone属性,并将值设置为7621 emp = (Element) emps.item(0); emp.setAttribute("telephone", "7654321");
//4.删除"申林"的telephone属性 emp = (Element) emps.item(2); emp.removeAttribute("telephone");
//5.删除名字为"实习生A"的employee节点 emp.getParentNode().removeChild(emps.item(3));
//重新写入修改后的XML文档 docToXML(doc,outUrl); } catch (ParserConfigurationException e) { e.printStackTrace(); }catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } |
H. 测试类DOMParserTest.java源码如下:
package parsersTest; /** * Test parsers. * Copyright 2010.9 NTCsoft(郑州蜂鸟科技有限公司) , Inc. All rights reserved. * @author ZhouYuan * @since 2010-9-24 * @version 1.0 */ //导入解析器 import xmlParsers.DOMParser; //导入实体类 import domain.Company; import domain.Department; import domain.Employee;
/** * Test DOMParser. */ publicclass DOMParserTest { publicstaticvoid main(String args[]){ //实例化解析器 DOMParser dp = new DOMParser();
//读XML测试方法 Company comp = dp.readXML("src/Company.xml"); System.out.println("公司名称:"+comp.getName()); System.out.println("公司地址:"+comp.getAddress()); for(int i =0; i < comp.getDept().length; i++){ Department department = comp.getDept()[i]; System.out.println("\t部门编号:"+department.getNumber() +"\t部门名称:"+department.getName()); for(int j = 0; j < department.getEmp().length; j++){ Employee employee = department.getEmp()[j]; System.out.println("\t\t部门成员:\n\t\t\t姓名:"+employee.getName() +"\t职位:"+employee.getPosition() +"\t联系方式:"+employee.getTelephone()); } }
//写XML测试方法 dp.writeXML("d:\\company.xml", comp);
//处理XML测试方法 dp.XMLHandle("src/Company.xml", "e:\\company.xml"); } }
|
3.3 DOM4J方式解析XML
A. 概述:
DOM4J是一个易用的,开源的库,用于XML,XPath,XSLT。它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX,JAXP。
B. 特点:
它合并了许多超出基本 XML 文档表示的功能,包括集成的 XPath 支持、XML Schema 支持以及用于大文档或流化文档的基于事件的处理。
它具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。
C. XMLPath简介:
XPath 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
1. 常用的路径表达式和实例,如表3-1所示:
表达式 | 附加说明 | 实例 |
nodename | 选取此节点的所有子节点 | company:选择company元素的所有子节点 |
/ | 从根节点选取 | /company:选择根元素company |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 | //employee:选择所有employee子元素,不论它们在文档中什么位置 |
. | 选取当前节点 | 当前节点为company时: .//employee表示选取company节点下节点名称为employee的所有子节点 |
.. | 选取当前节点的父节点 | 当前节点为employee时: ..表示employee的父节点department |
@ | 选取属性 | //@lang:选择所有属性名为lang的属性节点,不论它们在文档中什么位置 |
表 3-1
2. 常用谓语选择器,如表3-2所示:
实例 | 说明 |
/department/employee[1] | 选取department 元素的第一个 employee子元素。 |
/department/employee [last()] | 选择department元素下的最后一个employee子元素 |
/department/employee [last()-1] | 选择department元素下的倒数第二个employee子元素 |
/department/employee [position()<3] | 选取最前面的两个属于 department元素的子元素的 employee元素。 |
//employee[@telephone] | 选取所有拥有名为 telephone的属性的 employee元素。 |
//department[employee='实习生A'] | 选取所有 department元素,且其中子元素 employee的文本内容为实习生A。 |
表 3-2
D. 在解析过程中需要使用的包、接口和类:
1. 需要导入的3个外部JAR包,如图3-5所示。dom4j-1.6.1.jar可从sourceforge网站上获得;jaxen.jar可从java2s上获得,saxpath.jar也可从java2s上获得,以上jar包同时也可从服务器上获得。
图 3-5
2. 在解析过程中需要使用的包、接口和类:
//使用dom4j解析xml文档的辅助类 import org.dom4j.*; //需要使用util包下的list接口和Iterator接口 import java.util.*; //java标准输入输出控制 import java.io.*; //用于创建使用各种方式读取XML源和以各种方式输出XML源的解析器 import org.dom4j.io.*;
//导入实体类 import domain.Company; import domain.Department; import domain.Employee; |
E. 读取本地XML文档解析并转换为对象的步骤:
该步骤与DOM方式解析XML类似,只不过使用的API和操作方式不同。剑理一致,招式不同。
1. 创建负责把XML文档源文件解析为文档对象树的解析器。
//使用org.dom4j.io.包下DOM4J已经配置好的SAXReader读取XML文档。 SAXReader reader = new SAXReader(); //设置读取文件的编码为UTF-8 reader.setEncoding("utf-8"); //读取url指定的xml文件并返回一个新的DOM4J的document对象 Document doc = reader.read(url); |
2. 使用list方式和iterator方式遍历节点:
List方式: //通过Element的elements("nodeName")方法获得子节点并返回一个java标准列表List<Element> emps = deptEmt.elements("employee"); //根据得到的列表大小创建Employee对象数组 Employee[] empList = new Employee[emps.size()];
Iterator方式: //生成遍历department节点下名称为employee的所有元素节点的迭代器 Iterator empIte = root.elementIterator("department"); Element empEmt = (Element) empIte.next(); |
3. 遍历节点后将节点信息存储在java对象的实体类中,并返回该对象
F. 将java实体类对象转换为DOM4J文档对象并写入本地的步骤:
1. 创建XML文档对象
//使用DOM4J的DocumentHelper类的静态方法createDocument()创建一个XML文档对象。 Document doc = DocumentHelper.createDocument(); |
2. 使用DOM4J提供的节点操作方法创建节点对象并将java实体类对象中的信息存放至节点对象中。
3. 将XML对象转换和格式化后写入本地
//使用org.dom4j.io包下的XMLWriter类将DOM4J的文档树对象转换为XML流文件并格式化输出。 XmlWriter xmlWriter; //创建有格式和缩进的格式化输出对象 OutputFormat format = OutputFormat.createPrettyPrint(); //创建紧凑的无空白符和缩进的格式化输出对象 //OutputFormat format = OutputFormat.createCompactFormat(); format.setEncoding("UTF-8"); //将新的文件输出流对象和格式化对象封装进实例化的XMLWriter对象中 xmlWriter = new XMLWriter(new FileOutputStream(filePath),format); //使用xmlWriter将文档对象写入本地并关闭 xmlWriter.write(doc); xmlWriter.close(); |
G. 使用Xpath和DOM4J的节点处理方法处理文档:
1. 读取本地XML文档并转换为Document对象
2. 使用XPath获得需要处理的节点,并修改文档对象模型。
3. 将处理好的文档写入本地。
I. 解析器DOM4JParser.java的全部源码:
/** * Parse XML document with DOM4J. * Copyright 2010.9 NTCsoft(郑州蜂鸟科技有限公司) , Inc. All rights reserved. * @author ZhouYuan * @since 2010-9-24 * @version 1.0 */ package xmlParsers; //使用dom4j解析xml文档的辅助类 import org.dom4j.*; //需要使用util包下的list接口和Iterator接口 import java.util.*; //java标准输入输出控制 import java.io.*; //用于创建使用各种方式读取XML源和以各种方式输出XML源的解析器 import org.dom4j.io.*;
//导入实体类 import domain.Company; import domain.Department; import domain.Employee;
/** * Parse XML document with DOM4J.*/ publicclass DOM4JParser{
/** * 使用List方式遍历XML文档 * @param url XML文件路径 * @return返回Company对象 * @throws DocumentException 使用SAXReader读取XML文档时需要处理该异常 */ @SuppressWarnings("unchecked") public Company readXMLInList(String url) throws DocumentException{ //创建公司对象 Company comp = new Company(); //使用org.dom4j.io.包下DOM4J已经配置好的SAXReader读取XML文档。 SAXReader reader = new SAXReader(); reader.setEncoding("utf-8");//设置读取文件的编码为UTF-8 Document doc = reader.read(url); //读取url指定的xml文件 //得到文档的根元素company Element root = doc.getRootElement(); //使用Element的attributeValue(String arg)方法读取XML文档中的公司信息,并为公司对象设置相关属性。 comp.setAddress(root.attributeValue("address")); comp.setName(root.attributeValue("name"));
//通过elements(String arg)方法读取XML文档中的所有root节点下的department节点并返回一个List。 List<Element> depts = root.elements("department"); //创建Department对象数组 Department[] deptList = new Department[depts.size()];
//读取部门信息,并将部门对象添加至公司对象中。 for(int i = 0;i < depts.size();i++){ Department dept = new Department(); //使用List的get(int index)方法遍历List,返回列表中的第每一项。 Element deptEmt = depts.get(i); dept.setName(deptEmt.attributeValue("name")); dept.setNumber(deptEmt.attributeValue("deptNo"));
//通过Element的elements("nodeName")方法获得子节点并返回一个java标准列表对象 List<Element> emps = deptEmt.elements("employee"); //根据得到的列表大小创建Employee对象数组 Employee[] empList = new Employee[emps.size()]; /* * 读取雇员信息,并将雇员对象添加至部门对象中。*/ for(int j = 0;j < emps.size();j++){ Employee emp = new Employee(); //将XML中的employee节点转换为Employee对象emp,并设置对象属性。 Element empEmt = emps.get(j); emp.setName(empEmt.getText()); emp.setPosition(empEmt.attributeValue("position")); emp.setTelephone(empEmt.attributeValue("telephone")); //将emp对象添加至Employee对象数组empList中 empList[j]=emp; //将对象数组empList添加至部门对象dept的对象数组属性emp中。 dept.setEmp(empList); } //将dept对象添加至Department对象数组deptList中。 deptList[i]=dept; /* 将对象数组deptList添加至公司对象comp的对象数组属性deptList中, * 并返回该公司对象。*/ comp.setDept(deptList); } return comp; }
/** * 使用Iterator方式遍历XML文档 * @param url XML文件路径 * @return返回Company对象 * @throws DocumentException 使用SAXReader读取XML文档时需要处理该异常 */ @SuppressWarnings("unchecked") public Company readXMLInIterator(String url) throws DocumentException{ //创建公司对象 Company comp = new Company(); //使用org.dom4j.io.包下DOM4J已经配置好的SAXReader读取XML文档。 SAXReader reader = new SAXReader(); reader.setEncoding("utf-8");//设置读取文件的编码为UTF-8 Document doc = reader.read(url); //读取url指定的xml文件 //得到文档的根元素company Element root = doc.getRootElement(); //使用Element的attributeValue(String arg)方法读取XML文档中的公司信息,并为公司对象设置相关属性。 comp.setAddress(root.attributeValue("address")); comp.setName(root.attributeValue("name"));
//创建Department对象数组 Department[] deptList = new Department[(root.elements("department")).size()]; /* * 本段代码解释:读取部门信息,并将部门对象添加至公司对象中。 * for循环语句解释:生成遍历root节点下名称为department的所有元素节点的迭代器 */ for(Iterator deptIte = root.elementIterator("department");deptIte.hasNext();){ int i=0 ,j=0;//数组下标标志值 Department dept = new Department(); //使用List的get(int index)方法遍历List,返回集合中的第每一项。 Element deptEmt = (Element) deptIte.next(); dept.setName(deptEmt.attributeValue("name")); dept.setNumber(deptEmt.attributeValue("deptNo")); //创建Employee对象数组 Employee[] empList = new Employee[deptEmt.elements("employee").size()];
/* * 本段代码:读取雇员信息,并将雇员对象添加至部门对象中。 * for循环语句:生成遍历deptEmt节点下名称为employee的所有元素节点的迭代器 */ for(Iterator empIte = root.elementIterator("department");deptIte.hasNext();){ Employee emp = new Employee(); //将XML中的employee节点转换为Employee对象emp。 Element empEmt = (Element) empIte.next(); emp.setName(empEmt.getText()); emp.setPosition(empEmt.attributeValue("position")); emp.setTelephone(empEmt.attributeValue("telephone")); //将emp对象添加至Employee对象数组empList中 empList[j]=emp;j++; //将对象数组empList添加至部门对象dept的对象数组属性emp中。 dept.setEmp(empList); } //将dept对象添加至Department对象数组deptList中。 deptList[i]=dept;i++; /* 将对象数组deptList添加至公司对象comp的对象数组属性deptList中, * 并返回该公司对象。*/ comp.setDept(deptList); } return comp; }
/** * 将java实体类对象转换为DOM4J文档对象并写入本地 * @param url 文档写入路径 * @param comp 需要转换的java实体类对象 * @return成功时返回写入文件的url,失败时返回字符串Some Exception Occurred */ public String writeXML(String url,Company comp){ Document doc = DocumentHelper.createDocument(); //通过文档帮助工具创建一个新的文档对象。 Element root = doc.addElement("company") .addAttribute("name", comp.getName()) .addAttribute("address", comp.getAddress());//建立根节点company并添加至文档中
/* * 通过循环将公司对象中的部门对象数组转换成部门节点, * 并根据部门对象的属性设置部门节点的相关属性, * 最后将该部门节点添加至相应的公司节点中 */ for(int i = 0;i < comp.getDept().length;i++){ Department dept = comp.getDept()[i]; Element deptEmt = root.addElement("department") .addAttribute("deptNo", dept.getNumber()) .addAttribute("name", dept.getName()); /* * 通过循环将部门对象中的员工对象数组转换成员工节点, * 并根据员工对象的属性设置员工节点的相关属性, * 最后将该员工节点添加至相应的部门节点中 */ for(int j = 0;j < dept.getEmp().length;j++){ Employee emp = dept.getEmp()[j]; deptEmt.addElement("employee") .addAttribute("position", emp.getPosition()) .addAttribute("telephone", emp.getTelephone()) .addText(emp.getName()); } } //将文档对象写入本地 if(docToXML(doc,url)){ System.out.println("文件写入成功");return url; }else{ System.out.println("文件写入失败");return"Some Exception Occurred";} }
/** * 将DOM树对象转换为可以写入本地的XML文档对象 * @param doc 需要转换的DOM对象 * @param filePath 转换后的XML文档存放的 * @return写入成功返回true,转换过程中出现异常返回false */ publicboolean docToXML(Document doc,String filePath){ try { // 使用org.dom4j.io包下的XMLWriter类将DOM4J的文档树对象转换为XML流文件并格式化输出。 XMLWriter xmlWriter; //创建有格式和缩进的格式化输出对象 OutputFormat format = OutputFormat.createPrettyPrint(); //创建紧凑的无空白符和缩进的格式化输出对象 //OutputFormat format = OutputFormat.createCompactFormat(); //设置XML文档的输出编码 format.setEncoding("UTF-8"); // format.setIndent(true); // format.setNewlines(true); //将新的文件输出流对象和格式化对象封装进实例化的XMLWriter对象中 xmlWriter = new XMLWriter(new FileOutputStream(filePath),format); //使用xmlWriter将文档对象写入本地 xmlWriter.write(doc); xmlWriter.close(); returntrue; } catch (IOException e) { e.printStackTrace(); returnfalse; } }
/** * 处理XML文档: * 1.修改属性:修改company节点的address属性为"郑州市金水区文化路与丰产路交叉口向东50米SOHO世纪城" * 2.修改文本节点:工程师A的名字为王工程师 * 3.添加属性:为"许刚"添加telephone属性,并将值设置为7654321 * 4.删除属性:删除"申林"的telephone属性 * 5.删除节点:删除名字为"实习生A"的employee节点 * @param inUrl 要处理的XML文档的url * @param outUrl 处理后的XML文档存放的的url */ @SuppressWarnings("unchecked") publicvoid XMLHandle(String inUrl,String outUrl){
//获得XML文档的树对象 SAXReader reader = new SAXReader(); reader.setEncoding("utf-8"); Document doc; try { doc = reader.read(inUrl);
Element company = doc.getRootElement();//获得根节点 List empList = company.selectNodes("//employee");//获得所有的employee节点 //1.修改company节点的address属性 company.attributeValue("address", "郑州市金水区文产路交叉口向东50米SOHO世纪城");
//2.修改工程师A的名字为王工程师 Node developer = (Node) empList.get(1); developer.setText("王工程师");
//3.为"许刚"添加telephone属性,并将值设置为7654321 Element xugang = (Element) empList.get(0); xugang.addAttribute("telephone", "7654321");
//4.删除"申林"的telephone属性 Element shenlin = (Element) doc.selectSingleNode("//department[2]//employee[1]"); shenlin.remove(shenlin.attribute("telephone"));
//5.删除名字为"实习生A"的employee节点 Element shiXiSheng = (Element) doc.selectSingleNode("//department[2]//employee[2]"); Element par = (Element) shiXiSheng.getParent(); par.remove(shiXiSheng);
//重新写入修改后的XML文档 docToXML(doc,outUrl); System.out.println("----------------Success----------------"); } catch (DocumentException e) { System.out.println("----------------fail----------------"); e.printStackTrace(); } } } |
J. 测试类DOM4JParserTest.java源码如下:
package parsersTest; /** * Test DOM4JParser. * Copyright 2010.9 NTCsoft(郑州蜂鸟科技有限公司) , Inc. All rights reserved. * @author ZhouYuan * @since 2010-9-24 * @version 1.0 */ //解析过程中可能抛出此异常 import org.dom4j.DocumentException; //导入解析器 import xmlParsers.DOM4JParser; //导入实体类 import domain.Company; import domain.Department; import domain.Employee;
publicclass DOM4JParserTest {
publicstaticvoid main(String args[]) throws DocumentException{ //实例化解析器 DOM4JParser d4jp = new DOM4JParser();
//使用List读XML测试方法 Company comp = d4jp.readXMLInList("D:\\Company.xml"); System.out.println("公司名称:"+comp.getName()); System.out.println("公司地址:"+comp.getAddress()); for(int i =0; i < comp.getDept().length; i++){ Department department = comp.getDept()[i]; System.out.println("\t部门编号:"+department.getNumber() +"\t部门名称:"+department.getName()); for(int j = 0; j < department.getEmp().length; j++){ Employee employee = department.getEmp()[j]; System.out.println("\t\t部门成员:\n\t\t\t姓名:"+employee.getName() +"\t职位:"+employee.getPosition() +"\t联系方式:"+employee.getTelephone()); } }
//使用Iterator读XML测试方法 comp = d4jp.readXMLInList("src/Company.xml"); System.out.println("公司名称:"+comp.getName()); System.out.println("公司地址:"+comp.getAddress()); for(int i =0; i < comp.getDept().length; i++){ Department department = comp.getDept()[i]; System.out.println("\t部门编号:"+department.getNumber() +"\t部门名称:"+department.getName()); for(int j = 0; j < department.getEmp().length; j++){ Employee employee = department.getEmp()[j]; System.out.println("\t\t部门成员:\n\t\t\t姓名:"+employee.getName() +"\t职位:"+employee.getPosition() +"\t联系方式:"+employee.getTelephone()); } }
//写XML测试方法 d4jp.writeXML("D:\\company.xml", comp);
//处理XML测试方法 d4jp.XMLHandle("src/Company.xml", "F:\\company.xml"); } } |
四、实战
A. 创建一个新的工程,并创建如下所示的XML实例和对应的实体类对象。
Company.xml源码如下: <?xml version="1.0" encoding="utf-8"?> <bookstore> <book category="database"> <title lang="中文">数据库系统概论</title> <author sex="女">萨师煊</author> <author>王珊</author> <year>2000</year> </book>
<book category="datastructer"> <title lang="CH">数据结构</title> <author email="yanweimin@qq.com">严蔚敏</author> <year>2004</year> <price>16.00</price> </book>
<book category="java"> <title lang="英文">JAVA2核心技术卷1</title> <author>Cay S. Horstmann</author> <author>Cary Cornell</author> <year>2006</year> <price>88.00</price> </book> </bookstore> |
B. XML文件读取:使用DOM方式和DOM4J方式读取该XML文件并转换为JAVA实体类对象输出。
C. XML文件写入:使用DOM方式和DOM4J方式将JAVA实体类对象转换为XML文件写入本地。
D. XML文件操作:
1. 增加bookstore节点的address属性,并设置属性值为"河南新华书店"。
2. 为数据库系统概论这本书增加price节点,节点值为25.1。
3. 修改数据库系统概论的lang属性值为"CH"。
4. 修改严蔚敏的名字为"YanWeiMin"。
5. 删除类别为java的节点,及其子节点。
6. 删除数据结构这本书作者的email属性
五、结束语
因本人能力有限,文档中不免有许多不足之处,希望大家能够指出,共同讨论,共同进步。
Yesterday already passed, tomorrrow unknown, live inpresent!
Just do it!