会计考友 发表于 2012-8-4 12:44:44

JAVA认证:Java语言的XPathAPI

从 Java 轨范中发芽 XML
Elliotte Harold (elharo@metalab.unc.edu), 副教授, Polytechnic University
简介: XPath 表达式比繁琐的文档对象模子(DOM)导航代码要轻易编写得多。如不美观需要从 XML 文档中提守信息,最快捷、最简单的法子就是在 Java™ 轨范中嵌入 XPath 表达式。Java 5 推出了 javax.xml.xpath 包,这是一个用于 XPath 文档发芽的自力于 XML 对象模子的库。
如不美观要告诉别人买一加仑牛奶,您会怎么说?“请去买一加仑牛奶回来” 仍是 “畴前门出去,向左转,走三个街区向右转,再走半个街区向右转进入商铺。走向四号通道,沿通道走闻阕矧左,拿一瓶一加仑装的牛奶然后到收银台付款。再沿原路回家。” 简直太好笑了。只要在 “请去买一加仑牛奶回来” 的基本上稍加指示,大都成人都能自己买回牛奶来。
发芽说话和计较机搜索与此近似。直接说 “找一个 Cryptonomicon 的副本” 要比编写搜索某个数据库的具体逻辑轻易得多。因为搜索操作的逻辑很是相似,可以发现一种通用说话让您使用 “找到 Neal Stephenson 的所有著作” 这样的呼吁,然后编写对特定数据存储执行此类发芽的引擎。
XPath
在众多发芽说话之中,结构化发芽说话(SQL)是一种针对发芽特定类型的关系库而设计和优化的说话。其他不那么常见的发芽说话还有对象发芽说话(OQL)和 XQuery。但本文的主题是 XPath,一种为发芽 XML 文档而设计的发芽说话。好比,下面这个简单的 XPath 发芽可以在文档中找到作者为 Neal Stephenson 的所有图书的问题:

//book/title
作为对照,发芽同样信息的纯 DOM 搜索代码如 清单 1 所示:

会计考友 发表于 2012-8-4 12:44:45

JAVA认证:Java语言的XPathAPI

</p>清单 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 发芽很是简单://book。为了找出这些图书的问题,只要增添一步,表达式就酿成了 //book/title。最后,真正需要的是title 元素的文本节点孩子。这就要求再增添一步,完整的表达式就是//book/title/text()。
此刻我供给一个简单的轨范,它从 Java 说话中执行这个发芽,然后把找到的所有图书的问题打印出来。首先,需要将文档加载到一个 DOMDocument 对象中。为了简化起见,假设该文凳ё仝当前工作目录的 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/title/text()");

会计考友 发表于 2012-8-4 12:44:46

JAVA认证:Java语言的XPathAPI

直接求值
如不美观 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/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 数据模子
每当同化使用诸如 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.NODESETXPathConstants.BOOLEANXPathConstants.NUMBERXPathConstants.STRINGXPathConstants.NODE最后一个 XPathConstants.NODE 现实膳缦慊有匹配的 XPath 类型。只有知道 XPath 表达式只返回一个节点或者只需要一个节点时才使用它。如不美观 XPath 表达式返回了多个节得而且指定了 XPathConstants.NODE,则 evaluate() 按照文档挨次返回第一个节点。如不美观 XPath 表达式选择了一个空集并指定了 XPathConstants.NODE,则 evaluate() 返回 null。

如不美观不能完成要求的转换,evaluate() 将抛出 XPathException。

会计考友 发表于 2012-8-4 12:44:47

JAVA认证:Java语言的XPathAPI

</p>名称空间上下文
若 XML 文档中的元素在名称空间中,发芽该文档的 XPath 表达式必需使用不异的名称空间。XPath 表达式不必然要使用不异的前缀,只需要名称空间 URI 不异即可。事实上,如不美观 XML 文档使用默扰缦沱称空间,那么尽管方针文档没有使用前缀,XPath 表达式也必需使用前缀。
可是,Java 轨范不是 XML 文档,是以不能用一般的名称空间解析。必需供给一个对象将前缀映射到名称空间 URI。该对象是javax.xml.namespace.NamespaceContext 接口的实例。好比,假设图书文档放在 http://www.example.com/books 名称空间中,如 清单 5 所示:
清单 5. 使用默扰缦沱称空间的 XML 文档

            Snow Crash         Neal Stephenson         Spectra         0553380958         14.95            查找 Neal Stephenson 全数著作问题的 XPath 表达式就要改为 //pre:book/pre:title/text()。可是,必需将前缀 pre 映射到 URI http://www.example.com/books。NamespaceContext 接口在 Java 软件开发工具箱(JDK)或 JAXP 中没有默认实现似乎有点笨,但确实如斯。不外,自己实现也不难。清单 6 对一个名称空间给出了简单的实现。还需要映射xml 前缀。
清单 6. 绑定一个名称空间和默扰缦沱称空间的简单上下文

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();   }
页: [1]
查看完整版本: JAVA认证:Java语言的XPathAPI