UML软件工程组织

 

 

在 Ajax 中进行 XML 处理,第 2 部分: 两种使用 Ajax 和 XSLT 的方法
 
2008-05-22 作者:Mark Pruett 出处:IBM
 
本文内容包括:

前面 第 1 部分 主要介绍一些基础内容,同时审视第一种方法 —— 遍历 DOM 树。

在本系列的第 2 部分,Mark Pruett 介绍了 Asynchronous JavaScript + XML (Ajax) 天气面板的另外两种方法。这两种方法都使用可扩展样式表语言转换(XSLT) — 一种在服务器端,另一种在浏览器中。

在最后的 第 3 部分 中,将介绍实现天气面板的最后一种方法,利用 Web 公共服务 JavaScript Object Notation (JSON) 和动态脚本标记来实现。

本系列的 第 1 部分 提出了问题说明:建立便于插入任何 Web 页面的天气面板。天气面板采用 Ajax 技术实现,利用 United States National Weather Service (NWS) 提供的数据。NWS 数据以 XML 格式提供,每 15 分钟更新一次。

本系列文章分析了实现天气面板的四种不同方法。第一部分中介绍的一种办法是利用一种 Apache Web 服务器规则将 NWS XML 数据代理给浏览器。然后通过 JavaScript 代码从 DOM 提取需要的数据,转变为 HTML 格式再显示出来。

这一部分介绍第二和第三种方法。这两种办法有一点是共同的,即都使用 XSLT。

XSLT

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(有删减)
 
                
<?xml version="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'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 程序
 
                
<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-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/>

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, 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>

方法 2:XSLT 在服务器上

我打算在天气面板中采用 XSLT 方法。方法 2 中,服务器端脚本从 NWS 服务器获取数据,使用 XSLT 把 XML 转换为 HTML 然后将其返回到浏览器。浏览器将返回的 HTML 片段插入 DIV 标记。

图 1 显示了该方法中使用的数据管道。数据从 NWS 服务器流动到我的服务器,服务器端脚本将 XML 转换为 HTML。管道的重点是浏览器,接收 HTML 并插入到 Web 页面中。

图 1. 方法 2 的数据管道
方法 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";

# 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\n\n";
 print "Unknown location ($location).\n";
 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\n\n";

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 实现
 
                
function weather_badge (nws_id, div_name) {
 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 服务器上。如果访问服务器的用户少,或者服务器有大量内存和处理器周期可用,这种方法就能很好地工作。

类似的,如果确知用户使用落后的老式桌面电脑,这种方法也合适。浏览器没有多少工作:发送请求然后等待服务器完成大部分的任务。

方法 3:客户端 XSLT

天气面板的第三种实现也用到 XSLT。这一次 XSLT 处理在浏览器中完成。使用的 weather2html.xsl XSLT 程序(清单 2)仍然相同。图 2 显示了这种方法使用的数据管道。

图 2. 方法 3 的数据管道
方法 3 的数据管道

主流浏览器(Microsoft® Windows® Internet Explorer®、Firefox 和 Opera)都支持相同形式的 XSLT 处理。Firefox 和 Opera 实现了

清单 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) {

 // 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 处理。

结束语

本系列的 最后一部分 还要介绍一种方法 — 和前述的所有方法截然不同。差异如此大,您可能怀疑还是不是 Ajax。最后一种方法避免了上述三者的同一域问题,但是带来了一些新的问题。然后对这四种方法进行比较,并简要介绍其他的一些方法。

下载

描述 名字 大小 下载方法
本系列的示例代码
x-xmlajax.zip
194KB

参考资料

学习 获得产品和技术
  • IBM 试用版软件:用这些试用版软件开发您的下一个项目,可直接从 developerWorks 下载。
讨论
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号