- 浏览: 96181 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
xiaohee:
好文章,谢谢!
XML教程——XPath实例详解 -
hapychina:
static属性和方法要分配内存,不是static的就不分配了 ...
讨论:单例和静态方法的深入讨论 -
giianhui:
就目前的工作来说,我认为很有必要
深入JVM——方法区 -
hyj1254:
深有体会啊,搞java的很难通过证明自己技术有多nb来打动面试 ...
你适合学习java吗 -
wupuyuan:
我说说我的看法吧。1、物理内存角度来说,加载代码的内存消耗在整 ...
讨论:单例和静态方法的深入讨论
如果要告诉别人买一加仑牛奶,您会怎么说?“请去买一加仑牛奶回来” 还是 “从前门出去,向左转,走三个街区向右转,再走半个街区向右转进入商店。走向四号通道,沿通道走五米向左,拿一瓶一加仑装的牛奶然后到收银台付款。再沿原路回家。” 简直太可笑了。只要在 “请去买一加仑牛奶回来” 的基础上稍加指示,多数成人都能自己买回牛奶来。
查询语言和计算机搜索与此类似。直接说 “找一个 Cryptonomicon 的副本” 要比编写搜索某个数据库的详细逻辑容易得多。由于搜索操作的逻辑非常相似,可以发明一种通用语言让您使用 “找到 Neal Stephenson 的所有著作” 这样的命令,然后编写对特定数据存储执行此类查询的引擎。
在众多查询语言之中,结构化查询语言(SQL)是一种针对查询特定类型的关系库而设计和优化的语言。其他不那么常见的查询语言还有对象查询语言(OQL)和 XQuery。但本文的主题是 XPath,一种为查询 XML 文档而设计的查询语言。比如,下面这个简单的 XPath 查询可以在文档中找到作者为 Neal Stephenson 的所有图书的标题:
//book[author="Neal Stephenson"]/title |
作为对照,查询同样信息的纯 DOM 搜索代码如 清单 1 所示:
清单 1. 找到 Neal Stephenson 所有著作 title 元素的 DOM 代码
ArrayList result = new ArrayList(); NodeList books = doc.getElementsByTagName("book"); for (int i = 0; i < books.getLength(); i++) { Element book = (Element) books.item(i); NodeList authors = book.getElementsByTagName("author"); boolean stephenson = false; for (int j = 0; j < authors.getLength(); j++) { Element author = (Element) authors.item(j); NodeList children = author.getChildNodes(); StringBuffer sb = new StringBuffer(); for (int k = 0; k < children.getLength(); k++) { Node child = children.item(k); // really should to do this recursively if (child.getNodeType() == Node.TEXT_NODE) { sb.append(child.getNodeValue()); } } if (sb.toString().equals("Neal Stephenson")) { stephenson = true; break; } } if (stephenson) { NodeList titles = book.getElementsByTagName("title"); for (int j = 0; j < titles.getLength(); j++) { result.add(titles.item(j)); } } } |
不论您是否相信,清单 1 中的 DOM 显然不如简单的 XPath 表达式通用或者健壮。您愿意编写、调试和维护哪一个?我想答案很明显。
但是虽然有很强的表达能力,XPath 并不是 Java 语言,事实上 XPath 不是一种完整的编程语言。有很多东西用 XPath 表达不出来,甚至有些查询也无法表达。比方说,XPath 不能查找国际标准图书编码(ISBN)检验码不匹配的所有图书,或者找出境外帐户数据库显示欠帐的所有作者。幸运的是,可以把 XPath 结合到 Java 程序中,这样就能发挥两者的优势了:Java 做 Java 所擅长的,XPath 做 XPath 所擅长的。
直到最近,Java 程序执行 XPath 查询所需要的应用程序编程接口(API)还因形形色色的 XPath 引擎而各不相同。Xalan 有一种 API,Saxon 使用另一种,其他引擎则使用其他的 API。这意味着代码往往把您限制到一种产品上。理想情况下,最好能够试验具有不同性能特点的各种引擎,而不会带来不适当的麻烦或者重新编写代码。
于是,Java 5 推出了 javax.xml.xpath
包,提供一个引擎和对象模型独立的 XPath 库。这个包也可用于 Java 1.3 及以后的版本,但需要单独安装 Java API for XML Processing (JAXP) 1.3。Xalan 2.7 和 Saxon 8 以及其他产品包含了这个库的实现。
我将举例说明如何使用它。然后再讨论一些细节问题。假设要查询一个图书列表,寻找 Neal Stephenson 的著作。具体来说,这个图书列表的形式如 清单 2 所示:
<inventory> <book year="2000"> <title>Snow Crash</title> <author>Neal Stephenson</author> <publisher>Spectra</publisher> <isbn>0553380958</isbn> <price>14.95</price> </book> <book year="2005"> <title>Burning Tower</title> <author>Larry Niven</author> <author>Jerry Pournelle</author> <publisher>Pocket</publisher> <isbn>0743416910</isbn> <price>5.99</price> <book> <book year="1995"> <title>Zodiac</title> <author>Neal Stephenson<author> <publisher>Spectra</publisher> <isbn>0553573862</isbn> <price>7.50</price> <book> <!-- more books... --> </inventory> |
抽象工厂
XPathFactory
是一个抽象工厂。抽象工厂设计模式使得这一种 API 能够支持不同的对象模型,如 DOM、JDOM 和 XOM。为了选择不同的模型,需要向 XPathFactory.newInstance()
方法传递标识对象模型的统一资源标识符(URI)。比如 http://xom.nu/ 可以选择 XOM。但实际上,到目前为止 DOM 是该 API 支持的惟一对象模型。
查找所有图书的 XPath 查询非常简单://book[author="Neal Stephenson"]
。为了找出这些图书的标题,只要增加一步,表达式就变成了 //book[author="Neal Stephenson"]/title
。最后,真正需要的是 title
元素的文本节点孩子。这就要求再增加一步,完整的表达式就是 //book[author="Neal Stephenson"]/title/text()
。
现在我提供一个简单的程序,它从 Java 语言中执行这个查询,然后把找到的所有图书的标题打印出来。首先,需要将文档加载到一个 DOM Document
对象中。为了简化起见,假设该文档在当前工作目录的 books.xml 文件中。下面的简单代码片段解析文档并建立对应的 Document
对象:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); // never forget this! DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse("books.xml"); |
到目前为止,这仅仅是标准的 JAXP 和 DOM,没有什么新鲜的。
接下来创建 XPathFactory
:
XPathFactory factory = XPathFactory.newInstance(); |
然后使用这个工厂创建 XPath
对象:
XPath xpath = factory.newXPath(); |
XPath
对象编译 XPath 表达式:
PathExpression expr = xpath.compile("//book[author='Neal Stephenson']/title/text()"); |
直接求值
如果 XPath 表达式只使用一次,可以跳过编译步骤直接对 XPath
对象调用 evaluate()
方法。但是,如果同一个表达式要重复使用多次,编译可能更快一些。
最后,计算 XPath 表达式得到结果。表达式是针对特定的上下文节点计算的,在这个例子中是整个文档。还必须指定返回类型。这里要求返回一个节点集:
Object result = expr.evaluate(doc, XPathConstants.NODESET); |
可以将结果强制转化成 DOM NodeList
,然后遍历列表得到所有的标题:
NodeList nodes = (NodeList) result; for (int i = 0; i < nodes.getLength(); i++) { System.out.println(nodes.item(i).getNodeValue()); } |
清单 4 把上述片段组合到了一个程序中。还要注意,这些方法可能抛出一些检查异常,这些异常必须在 throws
子句中声明,但是我在上面把它们掩盖起来了:
清单 4. 用固定的 XPath 表达式查询 XML 文档的完整程序
import java.io.IOException; import org.w3c.dom.*; import org.xml.sax.SAXException; import javax.xml.parsers.*; import javax.xml.xpath.*; public class XPathExample { public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); domFactory.setNamespaceAware(true); // never forget this! DocumentBuilder builder = domFactory.newDocumentBuilder(); Document doc = builder.parse("books.xml"); XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); XPathExpression expr = xpath.compile("//book[author='Neal Stephenson']/title/text()"); Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; for (int i = 0; i < nodes.getLength(); i++) { System.out.println(nodes.item(i).getNodeValue()); } } } |
每当混合使用诸如 XPath 和 Java 这样两种不同的语言时,必定会有某些将两者粘合在一起的明显接缝。并非一切都很合拍。XPath 和 Java 语言没有同样的类型系统。XPath 1.0 只有四种基本数据类型:
- node-set
- number
- boolean
- string
当然,Java 语言有更多的数据类型,包括用户定义的对象类型。
多数 XPath 表达式,特别是位置路径,都返回节点集。但是还有其他可能。比如,XPath 表达式 count(//book)
返回文档中的图书数量。XPath 表达式 count(//book[@author="Neal Stephenson"]) > 10
返回一个布尔值:如果文档中 Neal Stephenson 的著作超过 10 本则返回 true,否则返回 false。
evaluate()
方法被声明为返回 Object
。实际返回什么依赖于 XPath 表达式的结果以及要求的类型。一般来说,XPath 的
- number 映射为
java.lang.Double
- string 映射为
java.lang.String
- boolean 映射为
java.lang.Boolean
- node-set 映射为
org.w3c.dom.NodeList
XPath 2
前面一直假设您使用的是 XPath 1.0。XPath 2 大大扩展和修改了类型系统。Java XPath API 支持 XPath 2 所需的主要修改是为返回 XPath 2 新数据类型增加常量。
在 Java 中计算 XPath 表达式时,第二个参数指定需要的返回类型。有五种可能,都在 javax.xml.xpath.XPathConstants
类中命名了常量:
XPathConstants.NODESET
XPathConstants.BOOLEAN
XPathConstants.NUMBER
XPathConstants.STRING
XPathConstants.NODE
最后一个 XPathConstants.NODE
实际上没有匹配的 XPath 类型。只有知道 XPath 表达式只返回一个节点或者只需要一个节点时才使用它。如果 XPath 表达式返回了多个节点并且指定了 XPathConstants.NODE
,则 evaluate()
按照文档顺序返回第一个节点。如果 XPath 表达式选择了一个空集并指定了 XPathConstants.NODE
,则 evaluate()
返回 null。
如果不能完成要求的转换,evaluate()
将抛出 XPathException
。
若 XML 文档中的元素在名称空间中,查询该文档的 XPath 表达式必须使用相同的名称空间。XPath 表达式不一定要使用相同的前缀,只需要名称空间 URI 相同即可。事实上,如果 XML 文档使用默认名称空间,那么尽管目标文档没有使用前缀,XPath 表达式也必须使用前缀。
但是,Java 程序不是 XML 文档,因此不能用一般的名称空间解析。必须提供一个对象将前缀映射到名称空间 URI。该对象是 javax.xml.namespace.NamespaceContext
接口的实例。比如,假设图书文档放在 http://www.example.com/books 名称空间中,如 清单 5 所示:
<inventory xmlns="http://www.example.com/books"> <book year="2000"> <title>Snow Crash</title> <author>Neal Stephenson</author> <publisher>Spectra</publisher> <isbn>0553380958</isbn> <price>14.95<price> </book> <!-- more books... --> <inventory> |
查找 Neal Stephenson 全部著作标题的 XPath 表达式就要改为 //pre:book[pre:author="Neal Stephenson"]/pre:title/text()
。但是,必须将前缀 pre
映射到 URI http://www.example.com/books。NamespaceContext
接口在 Java 软件开发工具箱(JDK)或 JAXP 中没有默认实现似乎有点笨,但确实如此。不过,自己实现也不难。清单 6 对一个名称空间给出了简单的实现。还需要映射 xml
前缀。
import java.util.Iterator; import javax.xml.*; import javax.xml.namespace.NamespaceContext; public class PersonalNamespaceContext implements NamespaceContext { public String getNamespaceURI(String prefix) { if (prefix == null) throw new NullPointerException("Null prefix"); else if ("pre".equals(prefix)) return "http://www.example.org/books"; else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI; return XMLConstants.NULL_NS_URI; } // This method isn't necessary for XPath processing. public String getPrefix(String uri) { throw new UnsupportedOperationException(); } // This method isn't necessary for XPath processing either. public Iterator getPrefixes(String uri) { throw new UnsupportedOperationException(); } } |
使用映射存储绑定和增加 setter 方法实现名称空间上下文的重用也不难。
创建 NamespaceContext
对象后,在编译表达式之前将其安装到 XPath
对象上。以后就可以像以前一样是用这些前缀查询了。比如:
XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); xpath.setNamespaceContext(new PersonalNamespaceContext()); XPathExpression expr = xpath.compile("//pre:book[pre:author='Neal Stephenson']/pre:title/text()"); Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; for (int i = 0; i < nodes.getLength(); i++) { System.out.println(nodes.item(i).getNodeValue()); } |
有时候,在 Java 语言中定义用于 XPath 表达式的扩展函数很有用。这些函数可以执行用纯 XPath 很难或者无法执行的任务。不过必须是真正的函数,而不是随意的方法。就是说不能有副作用。(XPath 函数可以按照任意的顺序求值任意多次。)
通过 Java XPath API 访问的扩展函数必须实现 javax.xml.xpath.XPathFunction
接口。这个接口只声明了一个方法 evaluate:
public Object evaluate(List args) throws XPathFunctionException |
该方法必须返回 Java 语言能够转换到 XPath 的五种类型之一:
String
Double
Boolean
Nodelist
Node
比如,清单 8 显示了一个扩展函数,它检查 ISBN 的校验和并返回 Boolean
。这个校验和的基本规则是前九位数的每一位乘上它的位置(即第一位数乘上 1,第二位数乘上 2,依次类推)。将这些数加起来然后取除以 11 的余数。如果余数是 10,那么最后一位数就是 X。
import java.util.List; import javax.xml.xpath.*; import org.w3c.dom.*; public class ISBNValidator implements XPathFunction { // This class could easily be implemented as a Singleton. public Object evaluate(List args) throws XPathFunctionException { if (args.size() != 1) { throw new XPathFunctionException("Wrong number of arguments to valid-isbn()"); } String isbn; Object o = args.get(0); // perform conversions if (o instanceof String) isbn = (String) args.get(0); else if (o instanceof Boolean) isbn = o.toString(); else if (o instanceof Double) isbn = o.toString(); else if (o instanceof NodeList) { NodeList list = (NodeList) o; Node node = list.item(0); // getTextContent is available in Java 5 and DOM 3. // In Java 1.4 and DOM 2, you'd need to recursively // accumulate the content. isbn= node.getTextContent(); } else { throw new XPathFunctionException("Could not convert argument type"); } char[] data = isbn.toCharArray(); if (data.length != 10) return Boolean.FALSE; int checksum = 0; for (int i = 0; i < 9; i++) { checksum += (i+1) * (data[i]-'0'); } int checkdigit = checksum % 11; if (checkdigit + '0' == data[9] || (data[9] == 'X' && checkdigit == 10)) { return Boolean.TRUE; } return Boolean.FALSE; } } |
下一步让这个扩展函数能够在 Java 程序中使用。为此,需要在编译表达式之前向 XPath 对象安装 javax.xml.xpath.XPathFunctionResolver
。函数求解器将函数的 XPath 名称和名称空间 URI 映射到实现该函数的 Java 类。清单 9 是一个简单的函数求解器,将扩展函数 valid-isbn
和名称空间 http://www.example.org/books 映射到 清单 8 中的类。比如,XPath 表达式 //book[not(pre:valid-isbn(isbn))]
可以找到 ISBN 校验和不匹配的所有图书。
iimport javax.xml.namespace.QName; import javax.xml.xpath.*; public class ISBNFunctionContext implements XPathFunctionResolver { private static final QName name = new QName("http://www.example.org/books", "valid-isbn"); public XPathFunction resolveFunction(QName name, int arity) { if (name.equals(ISBNFunctionContext.name) && arity == 1) { return new ISBNValidator(); } return null; } } |
由于扩展函数必须有名称空间,所以计算包含扩展函数的表达式时必须使用 NamespaceResolver
,即便查询的文档没有使用任何名称空间。由于 XPathFunctionResolver
、XPathFunction
和 NamespaceResolver
都是接口,如果方便的话可以将它们放在所有的类中。
用 SQL 和 XPath 这样的声明性语言编写查询,要比使用 Java 和 C 这样的命令式语言容易得多。但是,用 Java 和 C 这样的图灵完整语言编写复杂的逻辑,又比 SQL 和 XPath 这样的声明性语言容易得多。所幸的是,通过使用 Java Database Connectivity (JDBC) 和 javax.xml.xpath
之类的 API 可以将两者结合起来。随着世界上越来越多的数据转向 XML,javax.xml.xpath
将与 java.sql
一样变得越来越重要。
发表评论
-
设计模式之Visitor模式的应用(二)
2010-12-01 11:43 12751、 回顾 在上一篇文章中,我们给出了一个使用设计模式来改善 ... -
设计模式之Visitor模式的应用(一)
2010-12-01 11:42 11131、介绍 在进行项目的开发活动中,有一些设计在项目刚刚开始工 ... -
XML教程——XML四种解析器的比较
2010-11-26 14:21 10061: DOM DOM 是用与平台 ... -
XML教程——JAXP详解(二)
2010-11-25 19:54 1605在 JAXP 的早期版本中,该首字母缩写代表 Java API ... -
XML教程——JAXP详解(一)
2010-11-25 19:52 1943Java 技术和 XML 无疑是最 ... -
XML教程——采用DOM来解析XML
2010-11-25 15:35 893DOM基础 在开始使用 DOM ... -
XML教程——采用SAX来解析XML
2010-11-25 15:33 1315什么是 SAX 读取和操纵 XML 文件的标准方法是 DOM ... -
XML教程——XML解析器
2010-11-25 15:30 1638Parser基础 一个 XML Parser ... -
XML教程——XML Schema
2010-11-25 10:22 1228什么是XML Schema XML Schema如同DTD一 ... -
XML教程——XPath
2010-11-25 10:01 1005用XPath精确定位节点元 ... -
XML教程——XSL样式
2010-11-24 20:46 1501什么是样式单 对于一 ... -
XML教程——命名空间
2010-11-24 20:34 1117为何引入名称空间? XML的用途不是单一的。虽然读者可能看到 ... -
XML教程——DTD
2010-11-24 20:29 1030内部Dtd 一个“有效的 ... -
XML教程——XML简介
2010-11-24 20:22 956什么是XML XML即为可扩 ...
相关推荐
10. **XPath实例详解**:XPath的使用通常涉及到选取特定节点,例如选取所有`<title>`元素,或选取满足条件的元素,如所有价格高于特定值的书籍。 11. **JAXP(Java API for XML Processing)**:JAXP是Java中处理...
根据提供的信息,《无废话XML》是由两只老虎编写的关于XML技术的专业书籍,主要面向中文读者群体。...这些内容涵盖了XML的基础知识、语法结构、相关技术和应用实例,旨在帮助读者全面理解XML的核心概念和技术要点。
若要从XML文件中读取数据,你可以使用`XmlDocument`的`Load()`方法加载文件,然后使用XPath或LINQ to XML查询数据。 3. 修改XML文件: - 通过`XmlNode`和`XmlElement`对象,你可以找到并修改XML文档中的特定节点...
### XPath详解 #### 一、XPath简介 XPath是一种在XML文档中查找信息的语言。它用于在XML树结构中导航,并且能够返回所匹配节点或节点集。XPath的强大之处在于其简洁性和灵活性,允许用户轻松地从复杂的XML文档中...
本文将深入讲解XPath的基础用法,包括XPath的简介、教程、Axes、节点类型、实例、语法以及运算符。 1. **XPath简介** XPath是W3C标准的一部分,它的主要目的是为了提供一种简洁且相对独立的方式来定位XML文档中的...
### C语言库libxml2实例详解 #### 一、引言 libxml2是一个功能强大的C语言XML处理库,能够高效地解析、处理XML文件。本文档旨在通过具体实例介绍libxml2的基本使用方法,帮助读者更好地理解如何利用libxml2进行XML...
3.2.4 用dom解析xml文档实例 53 3.3 使用sax解析xml文档 65 3.3.1 sax的处理机制 66 3.3.2 配置sax解析器 69 3.3.3 sax解析器工厂 70 3.3.4 sax的异常类 71 3.3.5 errorhandler接口 73 3.3.6 使用sax解析xml...
3.2.4 用dom解析xml文档实例 53 3.3 使用sax解析xml文档 65 3.3.1 sax的处理机制 66 3.3.2 配置sax解析器 69 3.3.3 sax解析器工厂 70 3.3.4 sax的异常类 71 3.3.5 errorhandler接口 73 3.3.6 使用sax解析xml...
通过本教程的学习,参与者将能够掌握XML的基础知识及一系列重要的衍生技术,包括XML Schema、Namespaces、XPath、XSL 和 XSLT等。此外,教程还重点介绍了如何在Java程序中解析和处理XML文档,特别强调了这些技术在e-...
### Xpath教程详解 #### 一、XPath简介与节点类型 XPath是一种用于导航XML文档的语言。它使用路径表达式来选择XML文档中的节点或集合。XPath可以用来在XML文档中定位到任何位置,并且能够非常方便地提取所需的数据...
在上述代码中,我们创建了一个`NamespaceContext`实例,将"ss"前缀映射到对应的命名空间URI,然后在XPath表达式中使用这个前缀,使得XPath能够正确解析带命名空间的XML元素。 总的来说,XPath是Java中解析和操作XML...
3.2.4 用dom解析xml文档实例 53 3.3 使用sax解析xml文档 65 3.3.1 sax的处理机制 66 3.3.2 配置sax解析器 69 3.3.3 sax解析器工厂 70 3.3.4 sax的异常类 71 3.3.5 errorhandler接口 73 3.3.6 使用sax解析xml...
### XML程序设计开发实例知识点详解 #### XML与XPath基础 - **XML (Extensible Markup Language)**:一种标记语言,用于定义数据结构,并存储和传输数据。XML 的设计目的是传输和存储数据,而不是显示数据。 - **...
### Java与XML入门及处理实例详解 #### 一、XML简介 XML,即**可扩展标记语言**(Extensible Markup Language),是一种类似于HTML的标记语言,但它更多地被用于存储和传输数据,而不是像HTML那样主要用于定义和显示...
### XML网页编程开发详解 #### 一、XML与DTD概览 XML,即**可扩展标记语言**(Extensible Markup Language),是标准通用标记语言(SGML)的一个简化且更为灵活的子集,由万维网联盟(World Wide Web Consortium,...
**DOM4J解析XML实例详解** 在Java编程中,处理XML文档是一项常见的任务。DOM4J是一个非常流行的、强大的Java XML API,它提供了灵活且高效的方式来解析、创建、修改XML文档。本文将深入探讨如何使用DOM4J进行XML...
3.2.4 用dom解析xml文档实例 53 3.3 使用sax解析xml文档 65 3.3.1 sax的处理机制 66 3.3.2 配置sax解析器 69 3.3.3 sax解析器工厂 70 3.3.4 sax的异常类 71 3.3.5 errorhandler接口 73 3.3.6 使用sax解析xml...
接下来,我们通过几个实例来详细介绍如何使用Xpath来解析XML文件。首先是读取XML文件。在PHP中,我们可以使用simplexml_load_file()函数来载入XML文件,得到一个可以操作的SimpleXML对象。例如: ```php $xml = ...