创建 XSLT 样式表
现在,了解了将 XSLT 样式表应用到 XML 文档的方法后,可以创建样式表本身,将 Yahoo RSS 转换为 Google Mapplet 中的 KML。将对 Yahoo RSS 依次应用两个样式表。第一个样式表将其转换为更易管理的数据结构,第二个样式表将较简单的数据结构转换为最终的 KML。
使用 XSLT 简化数据结构
从 清单 10 中可以看出,Yahoo RSS 提要包含大量信息,结构有些复杂。另外,因为它来自外部组织,可能会随时改变数据的结构,因此,先将它转换为一个较简单的本地数据结构,可以不受这些改变影响。只要有一个中间 XSLT 样式表,可以将 Yahoo 的格式转换为本地格式,那么就可以设计最终的 KML,而不用考虑 Yahoo 的数据结构。创建中间样式表 yahoo-rss-to-simple-weather.xsl 的开始部分,如清单 23 所示。
清单 23. yahoo-rss-to-simple-weather.xsl
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
xmlns:yweather="http://weather.yahooapis.com/ns/rss/1.0">
</xsl:stylesheet>
|
创建样式表的骨架部分,在 xmlns:xsl 行为 XSLT 指定 XML 的名称空间。geo 名称空间包含在 Yahoo RSS 提要中,yweather 名称空间也包含在 RSS 提要中。如果不定义名称空间,那么样式表不能识别那些名称空间中的元素。
接下来,添加一个模板,匹配 RSS 提要的 <channel> 元素,如清单 24 所示。
清单 24. 与 /rss/channel 匹配的 XSLT 模板
<xsl:template match="/rss/channel">
<weather>
<data-source>
<link><xsl:value-of select="link"/></link>
<icon><xsl:value-of select="image/url"/></icon>
</data-source>
<xsl:apply-templates select="item/title"/>
<xsl:apply-templates mode="coordinates"
select="item"/>
<xsl:apply-templates mode="condition-icon"
select="item/description/img"/>
<xsl:apply-templates mode="units"
select="*[local-name()='units']"/>
<xsl:apply-templates mode="condition"
select="item/*[local-name()='condition']"/>
<xsl:apply-templates mode="wind"
select="*[local-name()='wind']"/>
<xsl:apply-templates mode="atmosphere"
select="*[local-name()='atmosphere']"/>
<xsl:apply-templates mode="astronomy"
select="*[local-name()='astronomy']"/>
</weather>
</xsl:template>
|
该模板匹配 /rss/channel 元素,生成结果文档中的顶级 <weather> 元素。创建 <data-source> 元素,指定信息源。<data-source> 元素包含一个 <link> 元素,指定信息源的 URL,它的值来自源文档的 /rss/channel/link 元素(Yahoo Weather 的链接)。另外,还包含一个 <icon> 元素,将包含 Yahoo Weather 图标的 /rss/channel/image/url 元素的值作为它的值。然后,对源文档中的一些 Xpath 应用模板 — 首先是 /rss/channel/item/title 元素,然后是一些其他的数据元素。
其他模板应用的不同之处在于它们都指定 mode 属性。<xsl:apply-templates> 接收 select 属性,它指定将作为参数传递到模板的节点的 Xpath。另外,还包含一个可选的 mode 属性。mode 属性可以改变您对待 XSLT 模板的方式。模式没有为模板提供特定的 match 属性,而是允许把这些属性当作函数调用,与一个没有模式的模板相比,模式会更紧密地匹配一个模板应用。为使模板规范更简单,对 <xsl:apply-templates> 的每一个调用都指定传入模板的节点和模式。如果样式表中的每个模板都给定自己的模式,那么就不用指定必须匹配的对象 — 可以用星号(*)字符进行匹配。例如,清单 25 显示坐标模式的模板。
清单 25. “coordinates” 模式的模板
<xsl:template mode="coordinates" match="*">
<coordinates>
<latitude><xsl:value-of select="geo:lat"/></latitude>
<longitude><xsl:value-of select="geo:long"/></longitude>
</coordinates>
</xsl:template>
|
因为该模板指定了一个模式,用星号(*)表示与任何节点匹配,如果使用这个模板的模式调用 <xsl:apply-templates>,将与该模板进行匹配。这将其转换为一个更常规的函数调用,而不用重新指定要应用模板的 Xpath 表达式。现在将由模板的调用方使用正确的 Xpath 表达式(必须这样做)调用它。该方法简化了模板规范,将模板转换为函数。另外,可以创建两个模板,应用于相同类型的节点,但是生成不同的结果。例如,使用 <item> 元素调用 coordinates 模板,生成 <item> 的 <geo:lat> 和 <geo:long> 元素的 <coordinates> 元素。还可以定义其他的模板,使用 <item> 元素进行调用,从中提取不同的信息。
回顾 清单 24 中的 /rss/channel 模板,可以了解为什么需要去掉源文档中的 CDATA 元素标记。其中一个模板应用是 <xsl:apply-templates mode="condition-icon"
select="item/description/img"/>。它使用 RSS 提要中的 <description> 的 <img> 标记调用 condition-icon 模板(使用这一模式的模板),因为要逐字输出模板,所以它最初包含在 CDATA 元素中。现在,去除 CDATA 标记后,该模板可以像获得源文档中其他 XML 元素一样获得 <img> 元素。
清单 24 中另外一个关注点是模板调用 <xsl:apply-templates mode="units"
select="*[local-name()='units']"/>。它使用 units 模式调用模板,传入不包括名称空间前缀,本地名称为 units 的所有节点。这是一种从其他名称空间选择节点的方法。在 Yahoo RSS 提要中,units 元素实际是一个 <yweather:units> 元素。Xpath 函数 local-name() 返回没有名称空间的元素的名称,因此可以更容易进行处理。在 yweather 名称空间的 condition、atmosphere 和 astronomy 元素中重复使用该方法。
units、condition、astronomy 和 atmosphere 模式的模板看起来相同,在结果文档中创建一个元素,将源元素的属性复制到该元素中,像 units 模式的模板中一样(参阅清单 26)。
清单 26. units 模式的模板
<xsl:template mode="units" match="*">
<units>
<xsl:copy-of select="@*"/>
</units>
</xsl:template>
|
使用 <yweather:units> 元素调用该模板,生成结果文档中的 <units> 元素,它包含源元素的所有属性(参阅 附加源代码 获得其他相似的模板定义)。
将较简单的数据结构转换为 KML 覆盖层
第一个样式表将 Yahoo RSS 文档转换为可以控制的 tamer 数据结构。转换格式后,XML 就可以被转换为 KML 文档。为实现这一功能,创建名为 simple-weather-to-kml.xsl 的样式表,如清单 27 所示。
清单 27. simple-weather-to-kml.xsl
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/weather">
<kml xmlns="http://earth.google.com/kml/2.2">
<Document>
<name><xsl:value-of select="title"/></name>
<description>Local Weather from Yahoo!</description>
<Placemark>
<name>Local Weather</name>
<description>
CDATA_START
<xsl:apply-templates mode="placemark-description"
select="."/>
CDATA_END
</description>
<xsl:apply-templates select="coordinates"/>
</Placemark>
</Document>
</kml>
</xsl:template>
</xsl:stylesheet>
|
它定义与根级别的 <weather> 元素匹配的模板,生成一个根级别的 <kml> 元素。KML 是一个指定地理信息的 XML 专用语,也可以指定地理覆盖层信息,例如地图上显示的点、线、文本或 HTML。可以使用 KML 创建一个天气信息的覆盖层,在 Google Maps 结果页面显示。KML 文档包含 <Document> 元素,<Document> 元素中包含 <name> 元素、<description> 元素,并且,在本例中,还包含一个或多个 <Placemark> 元素,每个元素都可成为地图上可单击的标记。另外,文档中还包含覆盖在地图上的线,用来绘制填充或未填充的多边形。
可以看到在 XsltChainController 的构造函数调用中指定的 CDATA_START 和
CDATA_END。它们表示结果文档中的 CDATA 部分的开始和结束,但是,该样式表不包含 CDATA 标记本身,否则 XSLT 处理程序将忽略 CDATA 部分的 <xsl:apply-templates> 元素。CDATA 部分包含的 HTML 内容将显示在 Placemark 的弹出气球中。HTML 内容在使用 placemark-description 模式的模板中被创建,如清单 28 所示。
清单 28. “placemark-description” 模板
<xsl:template mode="placemark-description" match="*">
<div>
<div>
<xsl:value-of select="condition/@text"/>,
<xsl:value-of select="condition/@temp"/>
<xsl:value-of select="units/@temperature"/>
</div>
<div>
Wind chill: <xsl:value-of select="wind/@chill"/>
<xsl:value-of select="units/@temperature"/> <br/>
Wind speed:
<xsl:value-of select="wind/@speed"/>
<xsl:value-of select="units/@speed"/>
</div>
<div>
Humidity: <xsl:value-of select="atmosphere/@humidity"/>%
</div>
<div>
Sunrise: <xsl:value-of select="astronomy/@sunrise"/> <br/>
Sunset: <xsl:value-of select="astronomy/@sunset"/>
</div>
</div>
</xsl:template>
|
该模板创建在地图标记的弹出气球中显示的 HTML,列出本地天气的所有信息。请注意,如何在 <xsl:value-of select="condition/@text"/> 中访问属性,这将输出 <condition> 子元素的 text 属性的值。该模板也使用 <units> 元素,其中包含每个计量的单位和它们的值。
清单 27 中的根级别的 <weather> 元素也对源文档(<xsl:apply-templates select="coordinates"/>)中的 <coordinates> 元素应用模板,生成 placemark 的坐标,如清单 29 所示。
清单 29. 与 <coordinates> 元素匹配的模板
<xsl:template match="coordinates">
<Point>
<coordinates>
<xsl:value-of select="longitude"/>,<xsl:value-of select="latitude"/>
</coordinates>
</Point>
</xsl:template>
|
该模板生成结果文档中的 <Point> 元素,指定 placemark 的经度和纬度对 — 在 Google Map 中的位置。
要查看应用样式表的结果,在浏览器中访问 http://YOUR-SERVER/weather.php?zipcode=10010&units=f,查看页面源代码,类似于清单 30。
清单 30. 最终的 KML 覆盖层
<?xml version="1.0"?>
<kml xmlns="http://earth.google.com/kml/2.2">
<Document>
<name>Conditions for Maspeth, NY at 12:18 am EDT</name>
<description>Local Weather from Yahoo!</description>
<Placemark>
<name>Local Weather</name>
<description><![CDATA[
<div>Mostly Cloudy, 57F</div>
<div>
Wind chill: 57F<br/>
Wind speed: 7mph
</div>
<div>
Humidity: 88%
</div>
<div>
Sunrise: 6:57 am<br/>
Sunset: 6:30 pm
</div>
]]></description>
<Point>
<coordinates>-73.91,40.72</coordinates>
</Point>
</Placemark>
</Document>
</kml>
|
请注意,这里的坐标远不及 Google Maps 中的坐标精确,意味着 placemark 实际上可能被放在地图中可见窗口的外面。为纠正这一问题,将地图适当缩小以确保 placemark 可见。
实际运行 KML
现在,整个 XSLT 链都可以发挥作用,可以在 Google mapplet 进行试验。单击 Reload 重新加载 Google 缓存中的 mapplet,选择 Server-side
XSLT 单选按钮,移动或缩放地图。Placemark 将在地图上显示。单击它,将会呈现 XSLT 转换创建的 HTML 天气报告,如图 8 所示。
图 8. Google Maps 中显示的本地天气 Placemark 气球
|