Java理论与实践:用XQuery举办屏幕汇集
副标题#e#
上个月,Java 技能讲师 Sam Pullara 向我演示了他最新的支持 Java 的电 话 Nokia 6630。这个手机利用了全面的技能 —— 嵌入式 JVM、GPRS 和蓝牙, 可是它也遭遇了所有智妙手机都苦恼的问题 —— 有限的屏幕实际利用区。有些 Web 站点支持基于手机的欣赏器,并且嵌入式欣赏器也试图在小小的屏幕上有效 地渲染页面,可是,在电话屏幕上查察典范的 Web 页面,就像要把一头大象强 行塞进车后座一样(个中的每个参加者城市感想失望,包罗您、车和大象)。 Sam 构建了一个简朴的、优雅的办理方案,从他喜欢的 Web 站点上对数据举办 屏幕汇集,然后把数据从头名目化,在小屏幕上显示。
新要领
从 HTML 文档提取数据的要领有很多种,可是我真的很喜欢 Sam 回收的要领 :既把 XQuery 看成屏幕汇集东西(从页面中提取相当的数据),又把它看成样 式表东西(从头名目化数据,以便数据适应页面,不需要举办页面转动)。只要 少量基本设施和一些很是简朴的 XQuery 表达式,就可以从大量数据源提取出相 关数据 —— 譬喻交通、天气和财政报价等,并在电话上完好地显示数据。
我已往常常处于这种环境:对 HTML 页面举办屏幕汇集对某些特定问题来说 好像是可行的方案,可是险些没有用于屏幕汇集的 Java 东西包。有很多 HTML 理会东西,但它们凡是缺少足够的抽象本领(把屏幕汇集代码弄得参差不齐), 大量不切合 HTML 类型的应用限制了它们,它们也无法处理惩罚那些布局大概随时间 产生变革的、动态生成的页面。
为了补充质量低下的 HTML 和富厚的 XML 处理惩罚东西之间的空缺,首先要把 HTML 转换成 XML。很多东西有助于完成这项事情;JTidy 东西包做得很好,可 以使这项事情变得轻松一些。JTidy 的设计方针是读入典范质量(即很糟)的 HTML 并输出更整洁的功效(有选项可供选择),它还提供了一个 DOM 接口,用 来遍历可以或许发送给 XML 理会器的 HTML 文档。清单 1 中的代码将从 InputStream 中读取 HTML 文档,并生成文档的 DOM 暗示:
清单 1. 用 JTidy 把 HTML 转换成 XML 兼容的 DOM
Tidy tidy = new Tidy();
tidy.setQuiet(true);
tidy.setShowWarnings(false);
Document tidyDOM = tidy.parseDOM(inputStream, null);
用这个简朴的转换,就差不多能把每个 Web 页面都看成 XML 文档举办处理惩罚 ,还能用本身喜欢的任何 XML 东西(好比 SAX、XSL、XPath,等等)提取数据 。固然 XSL 大概是很明智的选择(因为其设计方针就是为了从 XML 文档中提取 信息并转换这些信息,以便显示它们),可是假如不相识 XSL 的话,它的进修 曲线就很难把握,纵然是最简朴的 XSL 转换也巨大得让人心烦。XPath 是处理惩罚 信息提取的一个好选择 —— XSL 和 XQuery 都用它举办内容选择,可以很容易 地利用 XPath 把需要的数据提取出来,然后对 HTML 举办名目化,可是 XQuery 会让这项东西越发容易。
XQuery:简介
XQuery 的设计方针是从大概很是大的 XML 数据会合提取数据。输入的数据 集不必是 XML 文档,固然它大概是 XML 文档,可是也大概是已经编入索引并保 存在 XML 数据库中的文档荟萃,甚至是一组干系数据库中的表。像 SQL 一样, XQuery 包括从多个数据会合提取数据、汇总数据、聚合数据和毗连数据的函数 。
就像 JSP、ASP 或 Velocity 这样的暗示性模板语言一样,XQuery 把两个域 (暗示域和计较域)中的元素组合成一种组合语法。功效,所有 XML 文档都自 动成为有效的 XQuery 表达式,并对自身举办评估。XQuery 还包括一些语言语 句(language statement),譬喻“for”和“let”,它们可以与 XML 元素混 合利用。
清单 2 显示了一个示例 XML 文档 bib.xml,它暗示一个书目。然后我们将 先容一些快速的 XQuery 表达式,让您对 XQuery 可以或许做什么形成一种认识,最 后我们将再转到屏幕汇集的示例上。要全面先容 XQuery 的语法和利用环境大概 要用几百页的篇幅,有关更具体的参考质料和示例,请参阅 参考资料 小节。
清单 2. 示例 XML 书目
<bib>
<book year="1994">
<title>TCP/IP Illustrated</title>
<author><last>Stevens</last><first>W.</firs t></author>
<publisher>Addison-Wesley</publisher>
<price> 65.95</price>
</book>
. . . more books . . .
</bib>
清单 3 显示了一个 XQuery 表达式,它选择 Addison-Wesley 在 1991 年以 后出书的所有书籍,提取它们的标题,并把标题名目化成前面有项目的记的 (<ul>)列表。大括号暗示从“暗示模式”(数据直接通报到输出 ,例 如 <ul> 和 <li> 标签)到“代码模式”的切换;然后在 return 子句之后当即举办从“代码模式”到“暗示模式”的隐式切换。
清单 3. 按照查询参数选择图书标题的 XQuery 表达式
#p#分页标题#e#
<ul>
{
for $b in doc("bib.xml")/bib/book
where $b/publisher = "Addison-Wesley" and $b/@year > 1991
return
<li>{ data($b/title) }</li>
}
</ul>
#p#副标题#e#
查询语法引入了“for”,凡是称之为“Flower 表达式”(来自 FLWOR,是 for-let-where-order-return 的缩写),该语法从文档中选择一系列 XML 节点 ,在该例中,用 XPath 选取了来自 bib.xml 文档的 <book> 节点集,然 后进一步过滤出与指定查询参数(出书商是 Addison-Wesley,出书日期是 1991 年之后)匹配的节点。对付选出的每个节点,将在 return 子句中计较表达式, 在这里是标志(<li> 标签)与代码(提取出每个 <book> 节点的 <title> 元素的内容)的殽杂。
这个简朴的 XQuery 示例描写了 XQuery 的几个方面 —— 某一文档中暗示 与代码的殽杂、XPath 的运用、子条件的运用($b 引用)、不凡的查询表达式 、XQuery 函数(data()),尚有一个事实:输出文档的布局不必与输入文档的 布局匹配。就在这个相当紧凑的、读起来不是很难的查询中,孕育着强大的处理惩罚 本领。
清单 4 显示了一个更简朴的 XQuery 表达式,它把书目中差异出书商的数量 ,在一个 <count> 元素中输出。像前一个示例一样,它用 XPath 表达式 选择一组节点,然后用 XQuery 函数选择惟一值,并计较节点的数量。它通过运 算得到一个数字 —— bib.xml,即文档中差异出书商的数量。
清单 4. 计较差异出书商数量的 XQuery 表达式
<count>
{
let $d := distinct-values(doc("bib.xml")/book/publisher)
return count($d)
}
</count>
这些示例只是 XQuery 可以或许执行的各类查询范例的很少一部门,提供这些例 子仅仅是为了让您对利用 XQuery 可以或许做的工作有些感受,以及提示您如何才气 用 XQuery 把 XML 文档转换本钱身选择的名目。固然 XQuery 的大部门成果主 要用于查询大型文档可能其他数据源,可是也可以利用 XQuery 很是简朴的子集 来对 HTML 文档举办屏幕汇集,为各类应用措施提取出需要的数据,譬喻在屏幕 巨细有限的设备(譬喻蜂窝电话)上显示有关的数据,可能建设一个 DIY 的门 户网站,聚积并显示来自多个站点的数据。
用 XQuery 举办屏幕汇集
对 Web 页面做屏幕汇集的很多挑战之一是:它们凡是没有可以自我标识的结 构,并且它们的布局大概跟着站点内容的编辑而变革,甚至有大概按照差异的请 求,在页面中插入差异的动态内容(譬喻告白内容)。因此,对付页面中哪一部 分的内容与要提取的数据相对应,凡是不得不举办揣摩。
股票价值
此刻,让我们从提取 Yahoo! 财经页面中 IBM 股票的当前价值开始 (http://finance.yahoo.com/q?s=IBM)。这个页面上有很多质料 —— 新闻标 题、告白、财经数据,等等,可是我想要的是股票的价值数据,它放在一个表格 单位格中,接近包括“Last Trade”的单位格。清单 5 中的查询语句将选择所 有文本内容中包括“Last Trade”的 <td> 节点,然后为每个节点(但愿 只有一个)输出一个包括后续 <td> 节点内容的表格行。内容是用 return 子句中的 data() 函数提取的;不然,不只仅会获得 <td> 节点 中的文本,还会获得所有的标志。(在这个查询中,惟一包括能力的部门是 text()[1] 这个部门;在这里,text() 函数匹配的是 <td> 元素中的所 有元素 —— 在这个例子只有一个元素,但 XQuery 并不知道这一点 —— 所以 必需进一步汇报它在举办文本匹配之前,必需选择第一个文本节点)。只要页面 包括一个表格单位格的文本是“Last Trade”,并且后续的单位格包括的是股票 价值,那么,纵然页面的布局随意变革,也不会造成查询失败。
清单 5. 从 Yahoo! 财经提取股票报价的 XQuery 表达式
<table>
{
for $d in //td
where contains($d/text()[1], "Last Trade")
return <tr><td> { data($d/following-sibling::td) } </td></tr>
}
</table>
天气
#p#分页标题#e#
此刻来试一下别的一个页面。Yahoo! 天气页面包括很多 portlet 面板,我 想提取上面所列都市的名称、温度和图标。(假如登录 Yahoo! 天气页面 http://weather.yahoo.com,则屏幕上会显示出在“我的 Yahoo!”中所选都市 的天气,不然会显示一些主要多半会的天气环境。)清单 6 显示了一个查询, 它查找包括文本“New York, NY”的子面板,然后导航到关闭表格(enclosing table),并选中所有行:
清单 6. 从 Yahoo! 天气提取天气信息的 XQuery 表达式
<table>
{
for $d in //td[contains(a/small/text(), "New York, NY")]
for $row in $d/parent::tr/parent::table/tr
where contains($d/a/small/text()[1], "New York")
return <tr><td>{data($row/td[1])}</td>
<td>{data($row/td[2])}</td>
<td>{$row/td[3]//img}</td> </tr>
}
</table>
然后,对付每一行,XQuery 会提取出三个相关的数据列 —— 都市名称、温 度和图标 —— 并输出一个相对简朴的表,表中只包括这三项信息。功效就是比 较紧凑地显示了所体贴都市的信息,适合在小屏幕上显示。功效如下所示:
Chicago, IL | 49…63 F | |
London, UK | 32…41 F | |
New York, NY | 36…44 F | |
San Francisco, CA | 52…67 F |
这个查询不像 清单 5 中的查询那么成果强大。它假设文本“New York, NY ”将在 small 元素中(这就是下次 Yahoo! 从头设计他们的页面时可以轻松更 改的那类标志)。并且,也很容易将“New York, NY”多次显示在天气页面上。 可是,可以多花些精神来开拓查询,从而减轻这些风险元素;正如很多开拓选择 一样,在查询的巨大性与查询的不变性之间会有一个衡量。
清单 5 和 清单 6 中所示的查询不是建造这些查询的惟一方法。利用更巨大 的 XPath 语法,清单 6 中的两个 for 子句可以归并到一个 XPath 表达式中, 那么整个 清单 5 就能酿成一个 XPath 表达式,而不是利用 FLWOR 的语法。如 果是 XPath 好手,那么大概会发明,利用越发面向 XPath 的方法会更容易一些 ,而有更多 SQL 履历的人则会发明 FLWOR 的语法更有吸引力。
东西
针对 HTML 页面执行 XQuery 表达式所需要的代码很是少。可以用 JTidy 库 来清理 HTML 文档,然后把它暗示成 DOM 工具(请参阅 清单 1)。Saxon XQuery 引擎被用来编译和执行针对文档 DOM 工具的查询。编译和执行一个针对 文档 DOM 暗示的 XQuery 查询只需要 6 行代码,如清单 7 所示:
清单 7. 用 Saxon 编译和执行 XQuery 表达式的代码
Configuration c = new Configuration();
StaticQueryContext qp = new StaticQueryContext(c);
XQueryExpression xe = qp.compileQuery(query);
DynamicQueryContext dqc = new DynamicQueryContext(c);
dqc.setContextNode(new DocumentWrapper(tidyDOM, url, c));
List result = xe.evaluate(dqc);
查询计较的功效是 DOM Element 的 List,您可以用本身喜欢的 DOM 哄骗技 术(可能最不喜欢的 DOM 操纵技能)把查询功效转酿成文档。
竣事语
固然 XQuery 是为了查询大型文档而设计的,可是对付转换简朴的文档,它 也是一个不错的东西。不管是把巨大的页面简化成在小屏幕上显示的页面,照旧 从多个页面提取元素,把它们聚合在本身的派别上,可能仅仅是因为无法通过其他编程方法得到数据才从 Web 页面上提取数据,XQuery 都提供了从 HTML 页面 汇集所需数据的相对简朴的要领。