a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 230|回复: 3

[专业语言] JAVA认证:Java语言的XPathAPI

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
从 Java 轨范中发芽 XML* |4 H" i3 {* d8 E
Elliotte Harold (elharo@metalab.unc.edu), 副教授, Polytechnic University
) h9 T0 E: z! e* B0 t4 F0 U% p简介: XPath 表达式比繁琐的文档对象模子(DOM)导航代码要轻易编写得多。如不美观需要从 XML 文档中提守信息,最快捷、最简单的法子就是在 Java™ 轨范中嵌入 XPath 表达式。Java 5 推出了 javax.xml.xpath 包,这是一个用于 XPath 文档发芽的自力于 XML 对象模子的库。
' ~% S4 b$ C, ]  q+ P/ J5 y8 W如不美观要告诉别人买一加仑牛奶,您会怎么说?“请去买一加仑牛奶回来” 仍是 “畴前门出去,向左转,走三个街区向右转,再走半个街区向右转进入商铺。走向四号通道,沿通道走闻阕矧左,拿一瓶一加仑装的牛奶然后到收银台付款。再沿原路回家。” 简直太好笑了。只要在 “请去买一加仑牛奶回来” 的基本上稍加指示,大都成人都能自己买回牛奶来。, E2 _" S1 f" s
发芽说话和计较机搜索与此近似。直接说 “找一个 Cryptonomicon 的副本” 要比编写搜索某个数据库的具体逻辑轻易得多。因为搜索操作的逻辑很是相似,可以发现一种通用说话让您使用 “找到 Neal Stephenson 的所有著作” 这样的呼吁,然后编写对特定数据存储执行此类发芽的引擎。
8 H' v  z* z' z8 M, x* }5 V) KXPath
/ `  \' }' S4 l  S- ^1 [4 N在众多发芽说话之中,结构化发芽说话(SQL)是一种针对发芽特定类型的关系库而设计和优化的说话。其他不那么常见的发芽说话还有对象发芽说话(OQL)和 XQuery。但本文的主题是 XPath,一种为发芽 XML 文档而设计的发芽说话。好比,下面这个简单的 XPath 发芽可以在文档中找到作者为 Neal Stephenson 的所有图书的问题:
; g0 W* C' `' W/ {! W! G* P: i+ _4 }; J/ c) G8 C  d# h/ N4 ]
//book[author="Neal Stephenson"]/title 9 H, n+ r9 w+ E9 E; H6 Q! D
作为对照,发芽同样信息的纯 DOM 搜索代码如 清单 1 所示:
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:44:45 | 显示全部楼层

JAVA认证:Java语言的XPathAPI

</p>清单 1. 找到 Neal Stephenson 所有著作 title 元素的 DOM 代码
: s7 L2 Y0 H/ U- T3 F0 ?2 G/ B: F& Z- d) g& B
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 表达式通用或者健壮。您愿意编写、调试和维护哪一个?我想谜底很较着。7 H" V. }7 H2 G3 Q9 R
可是虽然有很强的表达能力,XPath 并不是 Java 说话,事实上 XPath 不是一种完整的编程说话。有良多工具用 XPath 表达不出来,甚至有些发芽也无法表达。例如说,XPath 不能查找国际尺度图书编码(ISBN)磨练码不匹配的所有图书,或者找出境外帐户数据库显示欠帐的所有作者。幸运的是,可以把 XPath 连系到 Java 轨范中,这样就能阐扬两者的优势了:Java 做 Java 所擅长的,XPath 做 XPath 所擅长的。
  Y1 n6 N* w9 k. x: a直到比来,Java 轨范执行 XPath 发芽所需要的应用轨范编程接口(API)还因形形色色的 XPath 引擎而各不不异。Xalan 有一种 API,Saxon 使用另一种,其他引擎则使用其他的 API。这意味着代码往往把您限制到一种产物上。理想情形下,最好能够试验具有分歧机能特点的各类引擎,而不会带来不恰当的麻烦或者从头编写代码。( s& L# z1 {" z; \. n% y+ Z) ~2 m
于是,Java 5 推出了 javax.xml.xpath 包,供给一个引擎和对象模良自力的 XPath 库。这个包也可用于 Java 1.3 及往后的版本,但需要零丁安装 Java API for XML Processing (JAXP) 1.3。Xalan 2.7 和 Saxon 8 以及其他产物包含了这个库的实现。+ N6 Y! n: p$ Z8 T& @
一个简单的例子9 y4 o. P! T; {; o" L; a; J6 K8 [
我将举例声名若何使用它。然后再谈判一些细节问题。假设要发芽一个图书列表,寻找 Neal Stephenson 的著作。具体来说,这个图书列表的形式如 清单 2 所示:# L7 F: h' @. Z
清单 2. 包含图手札息的 XML 文档* f" d/ r- D0 j5 J* ]) V
. n* v6 @0 o) w' M% q1 Q
              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            抽象工场
9 q: P; u* x8 }8 r( yXPathFactory 是一个抽象工场。抽象工场设计模式使得这一种 API 能够撑持分歧的对象模子,如 DOM、JDOM 和 XOM。为了选择分歧的模子,需要向XPathFactory.newInstance() 体例传递标识对象模子的统一资本标识符(URI)。好比 http://xom.nu/ 可以选择 XOM。但现实上,到今朝为止 DOM 是该 API 撑持的惟一对象模子。( M6 T- M, d5 p1 q, A0 G' \$ t
查找所有图书的 XPath 发芽很是简单://book[author="Neal Stephenson"]。为了找出这些图书的问题,只要增添一步,表达式就酿成了 //book[author="Neal Stephenson"]/title。最后,真正需要的是title 元素的文本节点孩子。这就要求再增添一步,完整的表达式就是//book[author="Neal Stephenson"]/title/text()。
; \) e* e. Q8 S7 i6 k  A此刻我供给一个简单的轨范,它从 Java 说话中执行这个发芽,然后把找到的所有图书的问题打印出来。首先,需要将文档加载到一个 DOMDocument 对象中。为了简化起见,假设该文凳ё仝当前工作目录的 books.xml 文件中。下面的简单代码片段解析文档并成立对应的Document 对象:& m5 T' C/ z* M
3 W2 ?- t; ?6 `4 Z( N( P7 b
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); // never forget this! DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse("books.xml"); 到今朝为止,这仅仅是尺度的 JAXP 和 DOM,没有什么新奇的。
  v0 p# X1 ~& y! {7 R: p接下来建树 XPathFactory:' d& }, J3 a# z; d

, W$ W5 l5 s1 p. ?0 b# QXPathFactory factory = XPathFactory.newInstance(); 然后使用这个工场建树 XPath 对象:# W: R4 B4 ~  J( J9 q# ]
$ `1 d5 u. w* c  _/ f2 [- k* {# w1 I
XPath xpath = factory.newXPath(); XPath 对象编译 XPath 表达式:$ [; T  ]4 g2 A& s' m
3 @( F/ F& L, d; l  H0 i2 e9 K
PathExpression expr = xpath.compile("//book[author='Neal Stephenson']/title/text()");
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:44:46 | 显示全部楼层

JAVA认证:Java语言的XPathAPI

直接求值& Z4 t$ F9 w- a. Y# `
如不美观 XPath 表达式只使用一次,可以跳过编译轨范直接对XPath 对象挪用 evaluate() 体例。可是,如不美观统一个表达式要一再使用多次,编译可能更快一些。/ B7 l# K+ b5 {7 H7 u
最后,计较 XPath 表达式获得结不美观。表达式是针对特定的上下文节点计较的,在这个例子中是整个文档。还必需指定返回类型。这里要求返回一个节点集:  l1 m# [5 ~% k2 G' {3 U) {
) @0 o  e+ b8 u$ _- k' U- ^; Y4 y
Object result = expr.evaluate(doc, XPathConstants.NODESET); 可以将结不美观强制转化成 DOM NodeList,然后遍历列表获得所有的问题:
( e7 L9 S. S# s4 ?* d. |' ~9 J
% K* s8 O9 m9 V# i# g# o3 ~6 wNodeList nodes = (NodeList) result; for (int i = 0; i < nodes.getLength(); i++) { System.out.println(nodes.item(i).getNodeValue()); } 清单 4 把上述片段组合到了一个轨范中。还要注重,这些体例可能抛出一些搜检异常,这些异常必需在 throws 子句中声明,可是我在膳缦沔把它们袒护起来了:! L' {7 d6 ]4 E1 G+ }
清单 4. 用固定的 XPath 表达式发芽 XML 文档的完整轨范
: |- [: h$ `3 R
& g- o' ]4 t4 G' c' p# Eimport 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 数据模子
- T  G; {& H8 {* K& I0 ?$ `每当同化使用诸如 XPath 和 Java 这样两种分歧的说话时,必定会有某些将两者粘合在一路的较着接缝。并非一切都很合拍。XPath 和 Java 说话没有同样的类型系统。XPath 1.0 只有四种根基数据类型:# p% h; Z. y$ L) Y: J/ E8 S- D; C. x
◆ node-set
; r) B( e' W3 Q& F$ U+ C◆ number
$ z: a$ G* Y- E  o◆ boolean9 X+ C' G1 t2 s( a3 s
◆ string* m' K( I& G0 R3 b9 o
当然,Java 说话有更多的数据类型,搜罗用户界说的对象类型。
2 I9 Q. u) D- s2 h, Q大都 XPath 表达式,出格是位置路径,都返回节点集。可是还有其他可能。好比,XPath 表达式 count(//book) 返回文档中的图书数目。XPath 表达式 count(//book[@author="Neal Stephenson"]) > 10 返回一个布尔值:如不美观文档中 Neal Stephenson 的著作跨越 10 本则返回 true,否则返回 false。
0 K8 L6 {0 ~7 K: X0 j- Z9 devaluate() 体例被声明为返回 Object。现实返回什么依靠于 XPath 表达式的结不美观以及要求的类型。一般来说,XPath 的9 E5 |' M5 u. {1 h5 ]+ o* V
◆ number 映射为 java.lang.Double
# Y! \# ?# a, e4 t4 ^, z( b◆ string 映射为 java.lang.String9 ]1 q1 V3 V4 Q  I
◆ boolean 映射为 java.lang.Boolean
' P) B( b( |0 I% v6 [◆ node-set 映射为 org.w3c.dom.NodeList
& b6 D1 h/ D) C/ G. ^+ d. h# a4 ]XPath 24 |$ H& D2 K+ l/ u* H) ]- k
前面一向假设您使用的是 XPath 1.0。XPath 2 大大扩展和改削了类型系统。Java XPath API 撑持 XPath 2 所需的首要改削是为返回 XPath 2 新数据类型增添常量。! g% ^0 x/ q. B1 O( I  q4 v( [
在 Java 入彀较 XPath 表达式时,第二个参数指定需要的返回类型。有五种可能,都在 javax.xml.xpath.XPathConstants 类中命名了常量:- J3 P* P, B7 h" g5 J" M4 _

$ |8 l" E, Y* s/ d% M' r5 u% VXPathConstants.NODESETXPathConstants.BOOLEANXPathConstants.NUMBERXPathConstants.STRINGXPathConstants.NODE最后一个 XPathConstants.NODE 现实膳缦慊有匹配的 XPath 类型。只有知道 XPath 表达式只返回一个节点或者只需要一个节点时才使用它。如不美观 XPath 表达式返回了多个节得而且指定了 XPathConstants.NODE,则 evaluate() 按照文档挨次返回第一个节点。如不美观 XPath 表达式选择了一个空集并指定了 XPathConstants.NODE,则 evaluate() 返回 null。
+ z4 S, b8 c' h+ b* p8 K+ C9 B* P/ I. O) U- E* H$ Y
如不美观不能完成要求的转换,evaluate() 将抛出 XPathException。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:44:47 | 显示全部楼层

JAVA认证:Java语言的XPathAPI

</p>名称空间上下文
: n/ c7 j6 x; d4 ?' T& {若 XML 文档中的元素在名称空间中,发芽该文档的 XPath 表达式必需使用不异的名称空间。XPath 表达式不必然要使用不异的前缀,只需要名称空间 URI 不异即可。事实上,如不美观 XML 文档使用默扰缦沱称空间,那么尽管方针文档没有使用前缀,XPath 表达式也必需使用前缀。! T' A! k) p2 M! C/ C$ q
可是,Java 轨范不是 XML 文档,是以不能用一般的名称空间解析。必需供给一个对象将前缀映射到名称空间 URI。该对象是javax.xml.namespace.NamespaceContext 接口的实例。好比,假设图书文档放在 http://www.example.com/books 名称空间中,如 清单 5 所示:
$ q& M) j- s* q& Z清单 5. 使用默扰缦沱称空间的 XML 文档
+ s7 L* |: J2 c2 W  X& F. B" l2 u6 d4 d
              Snow Crash         Neal Stephenson         Spectra         0553380958         14.95            查找 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 前缀。
- e$ ~) P- u4 q& e: l' S清单 6. 绑定一个名称空间和默扰缦沱称空间的简单上下文
) {2 l4 ~8 r# m
3 c" L8 h: Q$ a* ?! pimport 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();     }
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-7 21:23 , Processed in 0.232975 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表