本系列文章分析了实现天气面板的四种不同方法。第一部分中介绍的一种办法是利用一种 Apache Web 服务器规则将 NWS XML 数据代理给浏览器。然后通过 JavaScript 代码从 DOM 提取需要的数据,转变为 HTML 格式再显示出来。
这一部分介绍第二和第三种方法。这两种办法有一点是共同的,即都使用 XSLT。
|
XSLT 是一种查询 XML 并将其转换成其他格式的语言。这恰恰是我们所要对天气数据做的工作 — 以 XML 格式存储,但需要某种对用户(或者浏览器)更友好的格式。 NWS 数据中有些是天气面板所不需要的。需要某种技术提取需要的数据。XSLT 可以同时满足这两方面的要求。
本教程不是为了详细介绍 XSLT。关于 XSLT 的更多信息请参阅 developerWorks 文章 “What kind of language is XSLT?”(参见 参考资料)。
和其他很多计算机语言不同,XSLT 语法是有效的 XML。如果习惯于 C、Java™、Perl 或 Python 语言,可能会造成一点麻烦。
由于这两种方法都使用 XSLT,我们首先来看看它。然后再介绍如何纳入总体解决方案。
首先看看 NWS XML 数据格式。清单 1 显示了压缩后的例子。
清单 1. 示例 NWS XML 数据文件 KNGU.xml(有删减)
< current_observation version ="1.0"
xmlns:xsd ="http://www.w3.org/2001/XMLSchema"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation =
"http://www.weather.gov/data/current_obs/current_observation.xsd" >
< credit > NOAA's National Weather Service </ credit >
< credit_URL > http://weather.gov/ </ credit_URL >
< image >
< url > http://weather.gov/images/xml_logo.gif </ url >
< title > NOAA's National Weather Service </ title >
< link > http://weather.gov </ link >
</ image >
< suggested_pickup > 15 minutes after the hour </ suggested_pickup >
< suggested_pickup_period > 60 </ suggested_pickup_period >
< location > Norfolk, Naval Air Station, VA </ location >
< station_id > KNGU </ station_id >
< latitude > 36.94 </ latitude >
< longitude > -76.28 </ longitude >
< observation_time >
Last Updated on Jan 7, 2:53 pm EST
</ observation_time >
< observation_time_rfc822 >
Mon, 7 Jan 2008 14:53:00 -0500 EST
</ observation_time_rfc822 >
< weather > Fair </ weather >
< temperature_string > 74 F (23 C) </ temperature_string >
< temp_f > 74 </ temp_f >
< temp_c > 23 </ temp_c >
< relative_humidity > 34 </ relative_humidity >
< wind_string > From the Southwest at 9 Gusting to 18 MPH </ wind_string >
< wind_dir > Southwest </ wind_dir >
< wind_degrees > 240 </ wind_degrees >
< visibility_mi > 10.00 </ visibility_mi >
< icon_url_base >
http://weather.gov/weather/images/fcicons/
</ icon_url_base >
< icon_url_name >
skc.jpg
</ icon_url_name >
< disclaimer_url > http://weather.gov/disclaimer.html </ disclaimer_url >
< copyright_url > http://weather.gov/disclaimer.html </ copyright_url >
< privacy_policy_url > http://weather.gov/notice.html </ privacy_policy_url >
</ current_observation >
我们只对 清单 1 中突出显示的数据感兴趣,因此 XSLT 的首要任务是提取需要的元素。接下来将这部分数据转换成 HTML 以显示到浏览器中。
清单 2 显示了达成这两个目标的 XSLT 程序。
清单 2. weather2html.xsl,天气数据 XSLT 程序
xmlns:xsl ="http://www.w3.org/1999/XSL/Transform" >
< xsl:output method ="html" />
< xsl:template match ="/current_observation" >
< center >
< b >< xsl:value-of select ="location" /></ b >< br />
< xsl:value-of select ="weather" />< br />
< xsl:variable name ="icon_url_base" select ="icon_url_base" />
< xsl:variable name ="icon_url_name" select ="icon_url_name" />
< img border ='0' src ='{$icon_url_base}{$icon_url_name}'/><br/>
<xsl:value-of select ="temperature_string" />< br />
Wind: < xsl:value-of select ="wind_string" />< br />
Humidity: < xsl:value-of select ="relative_humidity" /> % < br />
Visibility: < xsl:value-of select ="visibility_mi" /> miles < br />
< br />< span style ='font-size: 0.8em; font-weight: bold;' >
< xsl:value-of select ="observation_time" /></ span >< br />
</ center >
</ xsl:template >
</ xsl:stylesheet >
首先注意这一行:
<xsl:output method="html" /> |
它告诉 XSLT 处理程序输出的是 HTML 而不是默认的 XML 格式。
XSLT 程序看起来似乎是 HTML 和 XML 的混合,的确如此。任何以 <xsl:
开始的 XML 元素都是 XSLT 语言的语句。其他的则是直接输出的文本。
比如这一行:
<b><xsl:value-of select="location" /></b><br/> |
xsl:value-of
元素要求 XSLT 处理程序找到 XML 输入文件中的 location
元素,提取元素值,在 xsl:value-of
标记所在的位置输出。结果如下所示:
<b>Norfolk, Naval Air Station, VA</b><br/> |
和其他编程语言如 Perl、Ruby 一样,XSLT 的执行也是通过语言解释程序完成的。通常称之为 XSLT 处理程序。但 XSLT 不是一种通用 编程语言 — 只能转换一种 XML 数据文件。因此多数 XSLT 处理程序需要两个输入文件:XSLT 程序和转换的 XML 文件。
很多 Linux® 版本都包含称为 xsltproc
的 XSLT 命令行处理程序。和其他类似的工具一样,调试和优化 XSLT 脚本非常方便。
Xsltproc
需要两个参数:XSLT 程序及其操作的 XML。假设我已经下载了 Norfolk Naval Air Station in Virginia 的 NWS XML 数据到 KNGU.xml 文件中(KNGU 是第 1 部分所说的四字符气象站惟一标识符)。可以用该命令测试 清单 2 中的 XSLT 程序:
xsltproc weather2html.xsl KNGU.xml |
XSLT 处理程序把 weather2html.xsl 中的转换规则应用于输入文件 KNGU.xml 并把结果写入标准输出。结果如 清单 3 所示。
< b > Norfolk, Naval Air Station, VA </ b >< br >
Fair < br >
< img border ="0"
src ="http://weather.gov/weather/images/fcicons/skc.jpg" >< br >
57 F (14 C) < br >
Wind: From the West at 7 MPH < br >
Humidity: 58% < br >
Visibility: 7.00 miles < br >
< br >
< span style ="font-size: 0.8em; font-weight: bold;" >
Last Updated on Oct 12, 7:53 am EDT
</ span >
< br >
</ center >
我打算在天气面板中采用 XSLT 方法。方法 2 中,服务器端脚本从 NWS 服务器获取数据,使用 XSLT 把 XML 转换为 HTML 然后将其返回到浏览器。浏览器将返回的 HTML 片段插入 DIV 标记。
图 1 显示了该方法中使用的数据管道。数据从 NWS 服务器流动到我的服务器,服务器端脚本将 XML 转换为 HTML。管道的重点是浏览器,接收 HTML 并插入到 Web 页面中。
我需要一个能执行 XSLT 转换的服务器端程序。该程序用 Perl 编写,如 清单 4 所示,其他语言也很容易实现。该脚本通过浏览器上的 XMLHttpRequest
调用激活。Ajax JavaScript 传递给服务器端脚本一个参数:从 NWS 服务器检索 XML 数据所需要的四字符 NWS Station ID。
清单 4. weather_xml2html.cgi Perl 脚本
use strict ;
my $ DATA_DIR = " /var/www/html/xml_weather/data " ;
my $ XSL_FILE = " $DATA_DIR/weather2html.xsl " ;
my $ XSLTPROC = " /usr/bin/xsltproc " ;
my $ HTTP_BIN = " /usr/bin/wget -q -O - " ;
my $ URL_FORMAT = " http://www.nws.noaa.gov/data/current_obs/%s.xml " ;
# Get the NOAA location key:
my $ location = $ ENV{QUERY_STRING} ;
# Minimal sanity check of the location key:
if ($ location !~ / ^[ d w]{ 4 } $/) {
print " Content-type: text/html " ;
print " Unknown location ($location). " ;
exit 1 ;
}
# Build URL:
my $ url = sprintf ($ URL_FORMAT , $ location );
# Build Command :
my $ cmd = " $HTTP_BIN '$url' 2> /dev/null | $XSLTPROC $XSL_FILE - " ;
print " Content-type: text/html " ;
open ( my $ IPIPE , " $cmd | " );
while (<$ IPIPE >) {
print $ _ ;
}
close $ IPIPE ;
Apache Web 服务器运行 清单 4 所示的脚本。Apache QUERY_STRING
环境变量向脚本提供四字符的 Station ID。
Perl 脚本使用两个外部命令:xsltproc
和 wget
。Xsltproc
即前面所述的命令行 XSLT 处理程序。Wget
是一个免费工具(可从 Free Software Foundation 下载)。多数 Linux 发行版已经预安装了该程序。Wget
可从 Web 上抓取 Web 页面(或其他 Web 资源)。Perl 脚本使用 wget
从 NWS 服务器获取 XML 文件。
如果在 Linux 命令行中执行,Perl 脚本将构造一个命令管道,如下所示:
/usr/bin/wget -q -O - http://www.nws.noaa.gov/data/current_obs/KNGU.xml / | /usr/bin/xsltproc /var/www/html/xml_weather/data/weather2html.xsl - |
|
该命令的输出被读入 Perl 脚本并发送到标准输出。要记住,脚本是通过浏览器 JavaScript 代码调用 XMLHttpRequest
激活的,因此结果作为响应返回到浏览器。
和方法 1 不同,这里不需要 Apache 代理规则。weather_xml2html.cgi 脚本就像一个智能代理,而且由于脚本在服务器上,从而避免了第 1 部分所述的同一域 问题(参见 参考资料)。
服务器返回格式化的天气面板 HTML,因此客户端的天气面板库就很简单了。清单 5 显示第二种方法所用的 JavaScript 代码。
清单 5. weather_badge()
的 weather_badge_intel_proxy.js
实现
var ajax = new Ajax
("/cgi-bin/xml_weather/weather_xml2html.cgi?",
nws_id,
"GET",
function (req) ...{
var div = document.getElementById (div_name);
div.innerHTML = req.responseText;
}
);
ajax.request ();
}
weather_badge()
的这个版本只需要调用服务器脚本(提供适当的 NWS 站点 ID),然后使用 innerHTML
属性将返回的 HTML 插入 DIV 标记。
这种方法把更多的处理任务放在 Web 服务器上。如果访问服务器的用户少,或者服务器有大量内存和处理器周期可用,这种方法就能很好地工作。
类似的,如果确知用户使用落后的老式桌面电脑,这种方法也合适。浏览器没有多少工作:发送请求然后等待服务器完成大部分的任务。
天气面板的第三种实现也用到 XSLT。这一次 XSLT 处理在浏览器中完成。使用的 weather2html.xsl XSLT 程序(清单 2)仍然相同。图 2 显示了这种方法使用的数据管道。
主流浏览器(Microsoft® Windows® Internet Explorer®、Firefox 和 Opera)都支持相同形式的 XSLT 处理。Firefox 和 Opera 实现了 XSLTProcessor
对象。Internet Explorer 通过扩展 Document 模型实现 XSLT 处理。
清单 6 显示了方法 3 的 weather_badge()
实现。和方法 1 相同,必须设置 Apache Web 代理规则以便浏览器能够访问 NWS 服务器(要记住 Ajax 应用程序只能访问提交原始 Web 页面的同一台服务器上的数据。关于同一域问题请参阅第 1 部分) 。
清单 6. weather_badge()
的 weather_badge_cs_xslt.js
实现
// Get the XML file from the server.
var ajax = new Ajax ("/nws_currobs/" + nws_id + ".xml", "", "GET", null);
ajax.setAsync (false);
ajax.request ();
var xml_doc = ajax.req.responseXML;
// Get the XSLT from the server.
ajax = new Ajax ("/xml_weather/data/weather2html.xsl", "", "GET", null);
ajax.setAsync (false);
ajax.request ();
var xsl_doc = ajax.req.responseXML;
var div = document.getElementById (div_name);
// Use object detection to find out if we have
// Firefox/Mozilla/Opera or IE XSLT support.
if (typeof XSLTProcessor != "undefined") ...{
var xsl_proc = new XSLTProcessor ();
xsl_proc.importStylesheet (xsl_doc);
var node = xsl_proc.transformToFragment (xml_doc, document);
div.innerHTML = "";
div.appendChild (node);
}
else if (typeof xml_doc.transformNode != "undefined") ...{
div.innerHTML = xml_doc.transformNode (xsl_doc);
}
else ...{
div.innerHTML = "XSLT not supported in browser.";
}
}
方法 1 中需要从 NWS 服务器检索 XML 文件。这种新的方法也需要从我自己的服务器上检索 XSLT 文件。清单 6 的前半部分是提取这些文件到 weather_badge()
函数中的代码。分别作为 JavaScript Document 对象 xml_doc
和 xsl_doc
通过 XMLHttpRequest
返回。
weather_badge()
函数的后半部分应用 XSLT 转换。由于 Internet Explorer 的处理方式和其他浏览器不同,需要确定运行的浏览器类型。通过对象检测 来完成。利用 JavaScript typeof
运算符检查是否存在执行转换需要的对象。
如果定义了 XSLTProcessor
,则使用的是 Firefox 或 Opera 浏览器。于是实例化新的 XSLTProcessor
对象并导入 XSLT 样式表。然后利用该对象的 transformToFragment
方法转换 XML 天气数据。这种方法返回文档片段而不是 HTML 文本。就是说和前两种方法不同,不能使用 innerHTML
将结果插入 DIV 标记。办法很简单:通过 appendChild
很容易将得到的文档片段插入页面的 DOM 树。
若没有定义 XSLTProcessor
但定义了 transformNode
方法,则假定 JavaScript 程序在 Internet Explorer 中运行。这种情况下只需要一行代码来执行 XSLT 转换并将结果插入 DIV 标记:
div.innerHTML = xml_doc.transformNode (xsl_doc); |
第三种方法结合了前两种方法的一些部分。Apache Web 代理把 XML 返回浏览器进一步处理,XSLT 完成从 XML 到 HTML 的转换。
方法 1 只需要直接访问 DOM 树中的数据元素,与此相比,浏览器中的 XSLT 处理程序需要更多的计算资源。对于这个简单的例子,用户可能觉察不到额外的计算时间。但是如果 XML 非常大或者 XSLT 转换很复杂,用户可能难以忍受浏览器显示结果的延迟。
另一方面,手动操作大型 XML 文件的复杂 DOM 树可能造成 JavaScript 代码难以编写和维护。
还需要考虑用户使用的浏览器和计算机。是有足够内存和处理器能力的高端工作站还是老式的落后计算机?其答案决定了是否能够执行复杂的 JavaScript XSLT 处理。
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
本系列的示例代码 | x-xmlajax.zip | 194KB | HTTP |
关于下载方法的信息 |