




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
如果要告诉别人买一加仑牛奶,您会怎么说?“请去买一加仑牛奶回来” 还是 “从前门出去,向左转,走三个街区向右转,再走半个街区向右转进入商店。走向四号通道,沿通道走五米向左,拿一瓶一加仑装的牛奶然后到收银台付款。再沿原路回家。” 简直太可笑了。只要在 “请去买一加仑牛奶回来” 的基础上稍加指示,多数成人都能自己买回牛奶来。查询语言和计算机搜索与此类似。直接说 “找一个 Cryptonomicon 的副本” 要比编写搜索某个数据库的详细逻辑容易得多。由于搜索操作的逻辑非常相似,可以发明一种通用语言让您使用 “找到 Neal Stephenson 的所有著作” 这样的命令,然后编写对特定数据存储执行此类查询的引擎。XPath在众多查询语言之中,结构化查询语言(SQL)是一种针对查询特定类型的关系库而设计和优化的语言。其他不那么常见的查询语言还有对象查询语言(OQL)和 XQuery。但本文的主题是 XPath,一种为查询 XML 文档而设计的查询语言。比如,下面这个简单的 XPath 查询可以在文档中找到作者为 Neal Stephenson 的所有图书的标题:/bookauthor=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所示:清单 2. 包含图书信息的 XML 文档 Snow Crash Neal Stephenson Spectra 0553380958 14.95 Burning Tower Larry Niven Jerry Pournelle Pocket 0743416910 5.99 Zodiac Neal Stephenson Spectra 0553573862 7.50 抽象工厂XPathFactory是一个抽象工厂。抽象工厂设计模式使得这一种 API 能够支持不同的对象模型,如 DOM、JDOM 和 XOM。为了选择不同的模型,需要向XPathFactory.newInstance()方法传递标识对象模型的统一资源标识符(URI)。比如 http:/xom.nu/ 可以选择 XOM。但实际上,到目前为止 DOM 是该 API 支持的惟一对象模型。查找所有图书的 XPath 查询非常简单:/bookauthor=Neal Stephenson。为了找出这些图书的标题,只要增加一步,表达式就变成了/bookauthor=Neal Stephenson/title。最后,真正需要的是title元素的文本节点孩子。这就要求再增加一步,完整的表达式就是/bookauthor=Neal Stephenson/title/text()。现在我提供一个简单的程序,它从 Java 语言中执行这个查询,然后把找到的所有图书的标题打印出来。首先,需要将文档加载到一个 DOMDocument对象中。为了简化起见,假设该文档在当前工作目录的 books.xml 文件中。下面的简单代码片段解析文档并建立对应的Document对象:清单 3. 用 JAXP 解析文档 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 = pile(/bookauthor=Neal Stephenson/title/text();直接求值如果 XPath 表达式只使用一次,可以跳过编译步骤直接对XPath对象调用evaluate()方法。但是,如果同一个表达式要重复使用多次,编译可能更快一些。最后,计算 XPath 表达式得到结果。表达式是针对特定的上下文节点计算的,在这个例子中是整个文档。还必须指定返回类型。这里要求返回一个节点集:Object result = expr.evaluate(doc, XPathConstants.NODESET);可以将结果强制转化成 DOMNodeList,然后遍历列表得到所有的标题: 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 = pile(/bookauthor=Neal Stephenson/title/text(); Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; for (int i = 0; i 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.NodeListXPath 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。该对象是space.NamespaceContext接口的实例。比如,假设图书文档放在 /books 名称空间中,如清单 5所示:清单 5. 使用默认名称空间的 XML 文档 Snow Crash Neal Stephenson Spectra 0553380958 14.95 查找 Neal Stephenson 全部著作标题的 XPath 表达式就要改为/pre:bookpre:author=Neal Stephenson/pre:title/text()。但是,必须将前缀pre映射到 URI /books。NamespaceContext接口在 Java 软件开发工具箱(JDK)或 JAXP 中没有默认实现似乎有点笨,但确实如此。不过,自己实现也不难。清单 6对一个名称空间给出了简单的实现。还需要映射xml前缀。清单 6. 绑定一个名称空间和默认名称空间的简单上下文import java.util.Iterator;import javax.xml.*;import space.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 /books; else if (xml.equals(prefix) return XMLConstants.XML_NS_URI; return XMLConstants.NULL_NS_URI; / This method isnt necessary for XPath processing. public String getPrefix(String uri) throw new UnsupportedOperationException(); / This method isnt necessary for XPath processing either. public Iterator getPrefixes(String uri) throw new UnsupportedOperationException(); 使用映射存储绑定和增加 setter 方法实现名称空间上下文的重用也不难。创建NamespaceContext对象后,在编译表达式之前将其安装到XPath对象上。以后就可以像以前一样是用这些前缀查询了。比如:清单 7. 使用名称空间的 XPath 查询 XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); xpath.setNamespaceContext(new PersonalNamespaceContext(); XPathExpression expr = pile(/pre:bookpre: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。清单 8. 检查 ISBN 的 XPath 扩展函数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, youd 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+) check
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 供应链金融合同中担保资产范围及买卖双方风险分担
- 电力系统检修安全操作协议
- 高层管理人员内部股份转让及股权激励协议
- 2023年辅警招聘报名表
- 大学法学专业试题及答案
- 造价专业面试题目及答案
- 全球化与科技融合的新模式研究-洞察及研究
- 网媒专业考试题及答案
- 检验专业英语试题及答案
- 吊篮租赁公司半年工作总结
- 演唱会招商方案
- 河北信息技术学业水平考试试题集
- 压力容器使用单位安全总监题库
- 创业合伙人五份协议书模板
- 建筑工程消防查验检查表
- 2024-2025学年七年级语文上册第一单元测试卷(统编版2024新教材)
- 应征公民政治审查表
- FZ∕T 71006-2021 山羊绒针织绒线
- 慢性创面的治疗及护理课件
- 2023年上海交通大学招聘考试真题
- YY/T 0003-2023手动病床
评论
0/150
提交评论