本系列文章分析了实现天气面板的四种不同方法。第一部分中介绍的一种办法是利用一种 Apache Web 服务器规则将 NWS XML 数据代理给浏览器。然后通过 JavaScript 代码从 DOM 提取需要的数据,转变为 HTML 格式再显示出来。
这一部分介绍第二和第三种方法。这两种办法有一点是共同的,即都使用 XSLT。
XSLT
 |
常用缩写词
- DOM:文档对象模型(Document Object Model)
- HTML:超文本标记语言(Hypertext Markup Language)
- XML:可扩展标记语言(Extensible Markup Language)
- XSLT:可扩展样式表语言转换(Extensible Stylesheet Language Transformation)
|
|
XSLT 是一种查询 XML 并将其转换成其他格式的语言。这恰恰是我们所要对天气数据做的工作 — 以 XML 格式存储,但需要某种对用户(或者浏览器)更友好的格式。 NWS 数据中有些是天气面板所不需要的。需要某种技术提取需要的数据。XSLT 可以同时满足这两方面的要求。
本教程不是为了详细介绍 XSLT。关于 XSLT 的更多信息请参阅 developerWorks 文章 “What kind of language is XSLT?”(参见 参考资料)。
和其他很多计算机语言不同,XSLT 语法是有效的 XML。如果习惯于 C、Java™、Perl 或 Python 语言,可能会造成一点麻烦。
由于这两种方法都使用 XSLT,我们首先来看看它。然后再介绍如何纳入总体解决方案。
使用 XSLT 转换数据
首先看看 NWS XML 数据格式。清单 1 显示了压缩后的例子。
清单 1. 示例 NWS XML 数据文件 KNGU.xml(有删减)
<?
xmlversion="1.0"encoding="ISO-8859-1"
?>
<
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'sNationalWeatherService
</
credit
>
<
credit_URL
>
http://weather.gov/
</
credit_URL
>
<
image
>
<
url
>
http://weather.gov/images/xml_logo.gif
</
url
>
<
title
>
NOAA'sNationalWeatherService
</
title
>
<
link
>
http://weather.gov
</
link
>
</
image
>
<
suggested_pickup
>
15minutesafterthehour
</
suggested_pickup
>
<
suggested_pickup_period
>
60
</
suggested_pickup_period
>
<
location
>
Norfolk,NavalAirStation,VA
</
location
>
<
station_id
>
KNGU
</
station_id
>
<
latitude
>
36.94
</
latitude
>
<
longitude
>
-76.28
</
longitude
>
<
observation_time
>
LastUpdatedonJan7,2:53pmEST
</
observation_time
>
<
observation_time_rfc822
>
Mon,7Jan200814:53:00-0500EST
</
observation_time_rfc822
>
<
weather
>
Fair
</
weather
>
<
temperature_string
>
74F(23C)
</
temperature_string
>
<
temp_f
>
74
</
temp_f
>
<
temp_c
>
23
</
temp_c
>
<
relative_humidity
>
34
</
relative_humidity
>
<
wind_string
>
FromtheSouthwestat9Gustingto18MPH
</
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 程序
<
xsl:stylesheet
version
="1.0"
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-ofselect
="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/>
|
XSLT 处理程序
和其他编程语言如 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 所示。
清单 3. KNGU.xml XSLT 处理结果
<
center
>
<
b
>
Norfolk,NavalAirStation,VA
</
b
><
br
>
Fair
<
br
>
<
img
border
="0"
src
="http://weather.gov/weather/images/fcicons/skc.jpg"
><
br
>
57F(14C)
<
br
>
Wind:FromtheWestat7MPH
<
br
>
Humidity:58%
<
br
>
Visibility:7.00miles
<
br
>
<
br
>
<
span
style
="font-size:0.8em;font-weight:bold;"
>
LastUpdatedonOct12,7:53amEDT
</
span
>
<
br
>
</
center
>
方法 2:XSLT 在服务器上
我打算在天气面板中采用 XSLT 方法。方法 2 中,服务器端脚本从 NWS 服务器获取数据,使用 XSLT 把 XML 转换为 HTML 然后将其返回到浏览器。浏览器将返回的 HTML 片段插入 DIV 标记。
图 1 显示了该方法中使用的数据管道。数据从 NWS 服务器流动到我的服务器,服务器端脚本将 XML 转换为 HTML。管道的重点是浏览器,接收 HTML 并插入到 Web 页面中。
图 1. 方法 2 的数据管道

我需要一个能执行 XSLT 转换的服务器端程序。该程序用 Perl 编写,如 清单 4 所示,其他语言也很容易实现。该脚本通过浏览器上的 XMLHttpRequest
调用激活。Ajax JavaScript 传递给服务器端脚本一个参数:从 NWS 服务器检索 XML 数据所需要的四字符 NWS Station ID。
清单 4. weather_xml2html.cgi Perl 脚本
#
!
/
usr
/
bin
/
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
"
;

#
GettheNOAAlocationkey:
my
$
location
=
$
ENV{QUERY_STRING}
;

#
Minimalsanitycheckofthelocationkey:
if
($
location!~
/
^[
d
w]{
4
}
$/)
{
print
"
Content-type:text/html
"
;
print
"
Unknownlocation($location).
"
;
exit
1
;
}

#
BuildURL:
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 -
|
 |
使用外部程序 一些程序员可能对实现这么简单的一个 Ajax Web 服务使用外部程序感到吃惊。Comprehensive Perl Archive Network (CPAN) 资料库包含成千上万个 Perl 模块。其中很多可用于代替 wget 或 xsltproc 。但使用这两个工具,weather_xml2html.cgi 脚本更容易改为其他语言实现,如 Python 或 Ruby。多数脚本语言都能把外部程序的结果导入脚本。事实上,我建议您使用所选语言提供的库或者模块。 |
|
该命令的输出被读入 Perl 脚本并发送到标准输出。要记住,脚本是通过浏览器 JavaScript 代码调用 XMLHttpRequest
激活的,因此结果作为响应返回到浏览器。
和方法 1 不同,这里不需要 Apache 代理规则。weather_xml2html.cgi 脚本就像一个智能代理,而且由于脚本在服务器上,从而避免了第 1 部分所述的同一域 问题(参见 参考资料)。
服务器返回格式化的天气面板 HTML,因此客户端的天气面板库就很简单了。清单 5 显示第二种方法所用的 JavaScript 代码。
清单 5. weather_badge()
的 weather_badge_intel_proxy.js
实现
functionweather_badge(nws_id,div_name)
...
{
varajax=newAjax
("/cgi-bin/xml_weather/weather_xml2html.cgi?",
nws_id,
"GET",

function(req)...{
vardiv=document.getElementById(div_name);
div.innerHTML=req.responseText;
}
);
ajax.request();
}
weather_badge()
的这个版本只需要调用服务器脚本(提供适当的 NWS 站点 ID),然后使用 innerHTML
属性将返回的 HTML 插入 DIV 标记。
优缺点
这种方法把更多的处理任务放在 Web 服务器上。如果访问服务器的用户少,或者服务器有大量内存和处理器周期可用,这种方法就能很好地工作。
类似的,如果确知用户使用落后的老式桌面电脑,这种方法也合适。浏览器没有多少工作:发送请求然后等待服务器完成大部分的任务。
方法 3:客户端 XSLT
天气面板的第三种实现也用到 XSLT。这一次 XSLT 处理在浏览器中完成。使用的 weather2html.xsl XSLT 程序(清单 2)仍然相同。图 2 显示了这种方法使用的数据管道。
图 2. 方法 3 的数据管道

主流浏览器(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
实现
function
weather_badge(nws_id,div_name)
...
{

//GettheXMLfilefromtheserver.

varajax=newAjax("/nws_currobs/"+nws_id+".xml","","GET",null);
ajax.setAsync(false);
ajax.request();
varxml_doc=ajax.req.responseXML;

//GettheXSLTfromtheserver.

ajax=newAjax("/xml_weather/data/weather2html.xsl","","GET",null);
ajax.setAsync(false);
ajax.request();
varxsl_doc=ajax.req.responseXML;

vardiv=document.getElementById(div_name);

//Useobjectdetectiontofindoutifwehave
//Firefox/Mozilla/OperaorIEXSLTsupport.


if(typeofXSLTProcessor!="undefined")...{
varxsl_proc=newXSLTProcessor();
xsl_proc.importStylesheet(xsl_doc);
varnode=xsl_proc.transformToFragment(xml_doc,document);

div.innerHTML="";
div.appendChild(node);
}

elseif(typeofxml_doc.transformNode!="undefined")...{
div.innerHTML=xml_doc.transformNode(xsl_doc);
}

else...{
div.innerHTML="XSLTnotsupportedinbrowser.";
}
}
方法 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 |
---|