文档对象模型 (DOM) 是一个文档标准,对于完备的文档和复杂的应用程序,DOM 提供了大量灵活性。DOM标准是标准的。它很强壮且完整,并且有许多实现。这是许多大型安装的决定因素--特别是对产品应用程序,以避免在API发生改变时进行大量的改写。
以上是我在选择处理XML数据时之所以没有选择JDOM或者dom4j等其它面向对象的标准的原因,不过也由于DOM从一开始就是一种与语言无关的模型,而且它更趋向用于像C或Perl这类语言,没有利用Java的面向对象的性能,所以在使用的过程中也遇到了不少的麻烦,今天这里做一个小结。另外,我目前使用XML主要是作为数据传输的统一格式,并统一用户界面展示的接口,应用的面并不是很广,所以使用到的DOM的内容其实不多。
在准备使用它的时候,是做了充足的准备的,也有遇到困难的准备,所以一开始就有了一个简单的工具类来封装DOM对象使用时必要的公共方法,实际证明这样做是很明智的,一个简单的创建Document对象的操作,要是每次都需要写上5行以上代码,并且还要处理那些烦人的Exception,实在是会打击大家的积极性,所以在最初,做了一个XMLTool类,专门封装了如下的公共方法:
1、 Document对象创建(包括空的Document对象创建,以一个给定Node节点作为根节点创建。
2、 将一个规范的XML字符串转换成一个Document对象。
3、 从物理硬盘读取一个XML文件并返回一个Document对象。
4、 将一个Node对象转换成字符串。
其中每个方法都截获相关的DOM操作所抛出的异常,转换成一个RuntimeException抛出,这些异常在实际使用过程中,一般状况下其实都不会抛出,特别是象生成一个Document对象时的ParserConfigurationException、转换Node节点成字符串时要生成一个Transformer对象时的TransformerConfigurationException等等,没有必要在它们身上花时间精力。而且真就出了相关的异常的话,其实根本没有办法处理,这样的状况通常是系统环境配置有问题(比如必要的DOM实现解析器等包没有加入环境),所以包装该异常时只是很简要的获取其Message抛出。
代码如下:
/**
* 初始化一个空Document对象返回。
* @return a Document
*/
public static Document newXMLDocument() {
try {
return newDocumentBuilder().newDocument();
} catch (ParserConfigurationException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 初始化一个DocumentBuilder
* @return a DocumentBuilder
* @throws ParserConfigurationException
*/
public static DocumentBuilder newDocumentBuilder()
throws ParserConfigurationException {
return newDocumentBuilderFactory().newDocumentBuilder();
}
/**
* 初始化一个DocumentBuilderFactory
* @return a DocumentBuilderFactory
*/
public static DocumentBuilderFactory newDocumentBuilderFactory() {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
return dbf;
}
/**
* 将传入的一个XML String转换成一个org.w3c.dom.Document对象返回。
* @param xmlString 一个符合XML规范的字符串表达。
* @return a Document
*/
public static Document parseXMLDocument(String xmlString) {
if (xmlString == null) {
throw new IllegalArgumentException();
}
try {
return newDocumentBuilder().parse(
new InputSource(new StringReader(xmlString)));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 给定一个输入流,解析为一个org.w3c.dom.Document对象返回。
* @param input
* @return a org.w3c.dom.Document
*/
public static Document parseXMLDocument(InputStream input) {
if (input == null) {
throw new IllegalArgumentException("参数为null!");
}
try {
return newDocumentBuilder().parse(input);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 给定一个文件名,获取该文件并解析为一个org.w3c.dom.Document对象返回。
* @param fileName 待解析文件的文件名
* @return a org.w3c.dom.Document
*/
public static Document loadXMLDocumentFromFile(String fileName) {
if (fileName == null) {
throw new IllegalArgumentException("未指定文件名及其物理路径!");
}
try {
return newDocumentBuilder().parse(new File(fileName));
} catch (SAXException e) {
throw new IllegalArgumentException(
"目标文件(" + fileName + ")不能被正确解析为XML!\n" + e.getMessage());
} catch (IOException e) {
throw new IllegalArgumentException(
"不能获取目标文件(" + fileName + ")!\n" + e.getMessage());
} catch (ParserConfigurationException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 给定一个节点,将该节点加入新构造的Document中。
* @param node a Document node
* @return a new Document
*/
public static Document newXMLDocument(Node node) {
Document doc = newXMLDocument();
doc.appendChild(doc.importNode(node, true));
return doc;
}
/**
* 将传入的一个DOM Node对象输出成字符串。如果失败则返回一个空字符串""。
* @param node DOM Node 对象。
* @return a XML String from node
*/
public static String toString(Node node) {
if (node == null) {
throw new IllegalArgumentException();
}
Transformer transformer = newTransformer();
if (transformer != null) {
try {
StringWriter sw = new StringWriter();
transformer.transform(
new DOMSource(node),
new StreamResult(sw));
return sw.toString();
} catch (TransformerException te) {
throw new RuntimeException(te.getMessage());
}
}
return errXMLString("不能生成XML信息!");
}
/**
* 将传入的一个DOM Node对象输出成字符串。如果失败则返回一个空字符串""。
* @param node DOM Node 对象。
* @return a XML String from node
*/
public static String toString(Node node) {
if (node == null) {
throw new IllegalArgumentException();
}
Transformer transformer = newTransformer();
if (transformer != null) {
try {
StringWriter sw = new StringWriter();
transformer.transform(
new DOMSource(node),
new StreamResult(sw));
return sw.toString();
} catch (TransformerException te) {
throw new RuntimeException(te.getMessage());
}
}
return errXMLString("不能生成XML信息!");
}
/**
* 获取一个Transformer对象,由于使用时都做相同的初始化,所以提取出来作为公共方法。
* @return a Transformer encoding gb2312
*/
public static Transformer newTransformer() {
try {
Transformer transformer =
TransformerFactory.newInstance().newTransformer();
Properties properties = transformer.getOutputProperties();
properties.setProperty(OutputKeys.ENCODING, "gb2312");
properties.setProperty(OutputKeys.METHOD, "xml");
properties.setProperty(OutputKeys.VERSION, "1.0");
properties.setProperty(OutputKeys.INDENT, "no");
transformer.setOutputProperties(properties);
return transformer;
} catch (TransformerConfigurationException tce) {
throw new RuntimeException(tce.getMessage());
}
}
/**
* 返回一段XML表述的错误信息。提示信息的TITLE为:系统错误。之所以使用字符串拼装,主要是这样做一般
* 不会有异常出现。
* @param errMsg 提示错误信息
* @return a XML String show err msg
*/
public static String errXMLString(String errMsg) {
StringBuffer msg = new StringBuffer(100);
msg.append("<?xml version=\"1.0\" encoding=\"gb2312\" ?>");
msg.append("<errNode title=\"系统错误\" errMsg=\"" + errMsg + "\"/>");
return msg.toString();
}
/**
* 返回一段XML表述的错误信息。提示信息的TITLE为:系统错误
* @param errMsg 提示错误信息
* @param errClass 抛出该错误的类,用于提取错误来源信息。
* @return a XML String show err msg
*/
public static String errXMLString(String errMsg, Class errClass) {
StringBuffer msg = new StringBuffer(100);
msg.append("<?xml version=\"1.0\" encoding=\"gb2312\" ?>");
msg.append(
"<errNode title=\"系统错误\" errMsg=\""
+ errMsg
+ "\" errSource=\""
+ errClass.getName()
+ "\"/>");
return msg.toString();
}
/**
* 返回一段XML表述的错误信息。
* @param title 提示的title
* @param errMsg 提示错误信息
* @param errClass 抛出该错误的类,用于提取错误来源信息。
* @return a XML String show err msg
*/
public static String errXMLString(
String title,
String errMsg,
Class errClass) {
StringBuffer msg = new StringBuffer(100);
msg.append("<?xml version=\"1.0\" encoding=\"gb2312\" ?>");
msg.append(
"<errNode title=\""
+ title
+ "\" errMsg=\""
+ errMsg
+ "\" errSource=\""
+ errClass.getName()
+ "\"/>");
return msg.toString();
}
以上都是DOM的基本应用,所以就不一一详细说明了。
在实际使用过程中,有几种状况使用很频繁,但是DOM的接口的设计却使该操作很麻烦,所以分别添加了相应的处理方法。
其中最麻烦的要数获取一个节点的Text子节点文本信息了,如下的XML节点:
<element>
text
</element>
在拥有element节点对象时,要获取其中的文本信息"text",首先要获取element节点的子节点列表,要判断其是否存在子节点,如果存在,那么遍历其子节点找到一个TextNode节点,通过getNodeValue()方法来获取该文本信息,由于这里element节点没有信息时没有子节点,所以必须判断element节点是否存在子节点才能去访问真正包含了文本信息的TextNode节点,那么如果要处理的数据都是以这种形式给出的,就会增加大量的开发代码同时让开发工作枯燥无味,因此这里使用了一个默认的约定实现,就是,给出了一个公共方法,该方法取给定Node下的直接子节点的Text节点文本信息,如果不存在Text节点则返回null,这个约定虽然使该方法的使用有所限制,也可能导致错误使用该方法,但是,按实际使用的状况来看,这样的约定和使用方式是没有问题的,因为实际用到的都是上面举的例子的状况,代码:
/**
* 这个方法获取给定Node下的Text节点文本信息,如果不存在Text节点则返回null。
* 注意:是直接子节点,相差2层或2层以上不会被考虑。
* @param node a Node 一个Node。
* @return a String 如果给定节点存在Text子节点,则返回第一个访问到的Text子节点文本信息,如果不存在则返回null。
*/
public static String getNodeValue(Node node) {
if (node == null) {
return null;
}
Text text = getTextNode(node);
if (text != null) {
return text.getNodeValue();
}
return null;
}
/**
* 这个方法获取给定Node下的Text节点,如果不存在Text节点则返回null。
* 注意:是直接子节点,相差2层或2层以上不会被考虑。
* @param node a Node 一个Node。
* @return a Text 如果给定节点存在Text子节点,则返回第一个访问到的Text子节点,如果不存在则返回null。
*/
public static Text getTextNode(Node node) {
if (node == null) {
return null;
}
if (node.hasChildNodes()) {
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if (list.item(i).getNodeType() == Node.TEXT_NODE) {
return (Text) list.item(i);
}
}
}
return null;
}
上面代码将获取给定Node节点的直接Text子节点分开包装。
另一个很经常碰到的状况是,我希望直接定位到目标节点,获取该节点对象,而不需要通过一层一层的节点遍历来找到目标节点,DOM2接口中至少提供了如下的方式来定位节点:
1、 对于Document对象:
1) getDocumentElement()――获取根节点对象,实际很少使用的,因为根节点基本也就只是根节点而已,实际的数据节点都是根节点下的直接子节点开始的。
2) getElementById(String elementId)――这个方法本来应该是一个最佳的定位方法,但是在实际使用过程中没有被我使用,其主要原因就是,这里的"ID"不同于一个节点的属性"ID",这在org.w3c.dom.Document的API说明中是明确指出,而我找了不少的资料也没有看到有关的使用方式,所以只好放弃了。
3) getElementsByTagName(String tagname)――这个方法其实是没有办法的选择,只好用它了,不过实际倒也很合用,虽然该方法返回的是一个NodeList,但是实际使用时,将节点的tagName设计成特殊字符串,那么就可以直接获取了,而实际使用时,其实也差不多,很多时候会直接拿数据库中的字段名来作为tagName,以方便得获取该字段得值,在一个简单得约定下,使用了如下方法:
/**
* 这个方法检索参数element下所有TagName为:tagName的节点,并返回节点列表的第一个节点。
* 如果不存在该tagName的节点,则返回null。
* @param element 待搜索节点
* @param tagName 待搜索标签名
* @return a Element 获得以tagName为标签名的节点列表的第一个节点。
*/
public static Element getFirstElementByName(
Element element,
String tagName) {
return (Element) getFirstElement(element.getElementsByTagName(tagName));
}
/**
* 从给定节点列表中获取第一个节点返回,如果节点集合为null/空,则返回null。
* @param nodeList a NodeList
* @return a Node
*/
private static Node getFirstElement(NodeList nodeList) {
if (nodeList == null || nodeList.getLength() == 0) {
return null;
}
return nodeList.item(0);
}
这个约定看似限制很大,其实实际使用时基本都是这样的,只要获取第一个给定tagName的Element节点就可以了的。
4)getElementsByTagNameNS(String namespaceURI, String localName)――这个方法基本没有使用,因为还没有碰到需要使用命名空间的状况。
2、 对于Element对象――――Element对象和Document对象雷同,少了getDocumentElement()方法,不过和Document一样也都是主要使用getElementsByTagName()方法。
3、 其它的节点对象基本没有直接定位的访问方法
还有一种,是由于DOM2的限制导致的,DOM2规范中,不能将一个Document docA的节点直接加入到另一个Document docB对象的节点的子节点列表中,要这么做必须首先将docA的节点通过docB的importNode方法转换后在添加到目标节点的子节点列表中,所以也有一个方法来统一处理:
/**
* 这个方法将参数appendedDoc的根节点及其以下节点附加到doc的跟节点下面。
* 作为doc的跟节点的作后一个子节点。
* 相当于:doc.appendDoc(appendedDoc);
* @param doc a Document
* @param appendedDoc a Document
*/
public static void appendXMLDocument(Document doc, Document appendedDoc) {
if (appendedDoc != null) {
doc.getFirstChild().appendChild(
doc.importNode(appendedDoc.getFirstChild(), true));
}
}
/**
* 这个方法将参数appendedDoc的根节点及其以下节点附加到node节点下面。
* 作为node节点的作后一个子节点。
* 相当于:node.appendDoc(appendedNode);
* @param node 待添加的节点将被添加到该节点的最后。
* @param appendedNode a Node 这个节点将被添加作为node节点的最后一个子节点。
*/
public static void appendXMLDocument(Node node, Node appendedNode) {
if (appendedNode == null) {
return;
}
if (appendedNode instanceof Document) {
appendedNode = ((Document) appendedNode).getDocumentElement();
}
node.appendChild(
node.getOwnerDocument().importNode(appendedNode, true));
}
基本上就这些常用的了,其它还有一些零碎的方法,不过都不常用到,就不作介绍了。另外要说的是,如果哪位知道上面说到的getElementById()方法的具体可行的方便用法,也请指教下。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=256164
分享到:
相关推荐
DOM解析器会将整个XML文件加载到内存中,形成一个树形结构,适合处理小到中等规模的XML文档。而SAX解析器采用事件驱动的方式,逐行读取XML文件,适合处理大型XML文件以避免内存消耗。 对于多级XML,DOM解析更便于...
1.11 小结 36 第2章 xml名称空间 38 2.1 声明名称空间 38 2.2 名称空间在元素和属性中的运用 39 2.2.1 名称空间在元素中的运用 39 2.2.2 默认名称空间 41 2.2.3 名称空间在属性中的运用 42 2.3 名称空间和dtd...
#### 五、小结 本文介绍了Java解析XML的两种主要方式:DOM和SAX。DOM适合于需要频繁访问和修改XML数据的场景,而SAX适用于数据量大且只需读取的场景。开发者可以根据实际需求选择合适的解析方式。此外,还介绍了...
**3.8 Java与XML联合编程之DOM篇** - **DOM解析** - 使用 DOM 对整个文档进行操作。 #### 四、其他 **4.1 代码复用的规则** - **代码复用** - 如何有效地重用代码。 **4.2 Java IO 包中的Decorator模式** - ...
在这个小结中,我们将深入探讨 Dom4j 的核心功能,包括初始化、使用迭代器、XPath 导航以及快速遍历。 1. 初始化: 在 Dom4j 中,解析 XML 文档的第一步是创建一个 `SAXReader` 实例,然后用它来读取 XML 文件。...
DOM4J作为Java处理XML的强大工具,通过简洁的API实现了对XML的全面操作。了解并熟练掌握DOM4J,对于提升XML处理能力大有裨益。同时,`dom4j-1.6.1.jar`库文件则是实际开发中使用DOM4J的依赖包,包含了所有相关功能的...
1.11 小结 36 第2章 xml名称空间 38 2.1 声明名称空间 38 2.2 名称空间在元素和属性中的运用 39 2.2.1 名称空间在元素中的运用 39 2.2.2 默认名称空间 41 2.2.3 名称空间在属性中的运用 42 2.3 名称空间和dtd...
1.11 小结 36 第2章 xml名称空间 38 2.1 声明名称空间 38 2.2 名称空间在元素和属性中的运用 39 2.2.1 名称空间在元素中的运用 39 2.2.2 默认名称空间 41 2.2.3 名称空间在属性中的运用 42 2.3 名称空间和dtd...
1.11 小结 36 第2章 xml名称空间 38 2.1 声明名称空间 38 2.2 名称空间在元素和属性中的运用 39 2.2.1 名称空间在元素中的运用 39 2.2.2 默认名称空间 41 2.2.3 名称空间在属性中的运用 42 2.3 名称空间和dtd...
在Java中,解析XML文档有多种方式,本文主要总结了四种常见的方法,包括DOM、SAX、StAX以及JAXB。每种方法都有其特点和适用场景,下面逐一介绍。 1. DOM(Document Object Model)解析: DOM是一种基于树形结构的...
DOM解析器将整个XML文档加载到内存中形成一个树形结构,适合小到中等规模的数据;SAX是事件驱动的,只在需要时处理数据,适用于大文件;而StAX是拉式模型,允许程序控制解析流程,适合流式处理。在学生管理系统中,...
2.3 本章小结 9 第三章 服务开发平台的总体框架 10 3.1 前台可视化IDE的设计 11 3.2 后台的总体设计 12 3.2.1 后台RuntimeContainer容器管理模块设计概述 13 3.2.2 后台UDDI注册中心模块设计概述 14 3.2.3 后台SP ...
本篇将总结DOM操作的基础知识,重点讲解如何添加元素以及使用节点属性。 1. DOM基础 DOM树中的节点类型包括: - Document:文档的根节点,所有其他节点都隶属于它。 - DocumentType:用于表示<!DOCTYPE>声明的...
1.5 小结 第2章 myedipse开发工具对各种框架的支持 2.1 使用jsp的两种模式 2.2 struts框架的实现 2.3 hibernate框架的实现 2.4 jpa框架的实现 2.5 spring框架的实现 2.6 jsf框架的实现 2.7...
书中阐述了XML 的基本概念、语法规则、文档类型定义(DTD)、Schema 结构、层叠样式单(CSS)、数据源对象(DSO)、文件转换(XSLT)、文档对象模型(DOM),还介绍了在Java、ASP 和.NET 以及电子商务环境下XML 的应用。...
#### 四、小结 通过上述介绍可以看出,在Java中加载文件的方式多种多样,开发者可以根据实际需求选择最适合的方法。无论是基于Java本身的类加载机制还是通过Spring等框架提供的便捷方式,都能够有效地实现资源文件...
**9.5 小结** XML在数据交换和存储中发挥了重要作用,尤其是在Web应用中。虽然XML数据存取速度相对较慢,且缺乏传统数据库的一些高级功能,但在某些场景下,如小规模数据和轻量级应用,XML可以直接作为数据库使用,...
##### 小结 通过上述步骤,我们可以构建一个基本的权限管理系统。这种方法简单易懂,适合于小型项目或初学者学习。然而,在实际应用中,随着项目的复杂度增加,可能需要更高级的技术和框架(如Spring Security)来...