级别: 初级 Benoit Marchal (bmarchal@pineapplesoft.com), 顾问, Pineapplesoft
2003 年 9 月 01 日 偶尔,您会得到某个太大以至于无法按现状发布的 XML 文件。要解决这个问题,您可以使用 XSLT 处理器将这个文件分解成较小的文档。这篇技巧文章向您演示如何使用流行的 XSLT 处理器做到这一点。
在以前的技巧文章(请参阅
参考资料)中,我已经解释了在样式表中如何将不同的 XML 文档合并。为了演示示例,我使用了一个由四个独立的 XML 文档组成的照片图库,四个独立的 XML 文档最终合并成一个 Web 页面。该技术也适于日志文件(将每天的日志合并成月度报告)和目录(将几章合并为一个目录(TOC))。
这篇技巧文章采用了不同的方法:如果您有一个 XML 文档,但要分成数页来输出,该如何做呢?您可以将长文档分解成数个较小的页面,这样就能较快地下载了。
一个照片图库
清单 1中的文档是一个包含对四张照片描述的照片小图库。您的任务是将其设计成一个小的网站。为了让下载的速度更快,每一张照片都应有它自己的一页。其难点在于要将原始文档分割成和照片数相同数量的页面。
在分割页面时,既有好消息又有坏消息。好消息是仅使用常规的 XSLT 处理器,您想把文档分成多少页面就能分成多少页面。坏消息是这(仍)不是一个标准功能;正如您将看到的那样,每一个 XSLT 处理器的实现都不同。幸运的是,这些不同之处都只是表面上的。
清单 1. gallery.xml -- a photo gallery in one XML document
<?xml version="1.0"?>
<gl:gallery xmlns:gl="http://ananas.org/2003/tips/gallery">
<gl:title>Flowers and plants</gl:title>
<gl:photo>
<gl:title>Rose</gl:title>
<gl:date>September 2002</gl:date>
<gl:image>rose.jpg</gl:image>
<gl:description>
This photo was taken early in the morning in a very soft light.
This rose flower was shot at a gas station on the highway!
</gl:description>
</gl:photo>
<gl:photo>
<gl:title>Orchid</gl:title>
<gl:date>May 2003</gl:date>
<gl:image>orchid.jpg</gl:image>
<gl:description>
This is a lady slipper (paphiopedilum liemianum) orchid.
In this shot, the flower is window-lit.
</gl:description>
</gl:photo>
<gl:photo>
<gl:title>Lily-of-the-valley</gl:title>
<gl:date>May 2003</gl:date>
<gl:image>lily.jpg</gl:image>
<gl:description>
The lily-of-the-valley is popular for its sweet perfume and is typically
associated with May 1st. This was done under artificial lights.
</gl:description>
</gl:photo>
<gl:photo>
<gl:title>Cardoon</gl:title>
<gl:date>September 2002</gl:date>
<gl:image>cardoon.jpg</gl:image>
<gl:description>
Also known as artichoke thistle, the cardoon is grown for its gentle
laxative properties. This specimen is dried and the colors have been
digitally altered to create a more graphic image.
</gl:description>
</gl:photo>
</gl:gallery>
|

 |

|
即时发布
清单 2 是发布照片图库的样式表。请特别注意
gl:photo 模板。该模板用
xalan:redirect 指令创建了一个单独的 HTML 页面,该页面自身作为一个文件存储。该样式表在 JDK 1.4.1 上进行过测试,并且只能和 JDK XSLT 处理器或 Xalan(Apache Xalan 是 JAXP 的参考实现)一起工作。
清单 2. jdk.xsl - 用于 JDK 1.4(及 Xalan)的样式表
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gl="http://ananas.org/2003/tips/gallery"
xmlns:xalan="org.apache.xalan.xslt.extensions.Redirect"
extension-element-prefixes="xalan">
<xsl:output method="html"/>
<xsl:template match="gl:gallery">
<html>
<head><title><xsl:value-of select="gl:title"/></title></head>
<body>
<h1><xsl:value-of select="gl:title"/></h1>
<p>The photos are <a href="photo-0.html">here</a>.</p>
</body>
</html>
<xsl:apply-templates select="gl:photo"/>
</xsl:template>
<xsl:template match="gl:photo">
<xalan:write select="concat('photo-',position(),'.html')">
<html>
<head><title><xsl:value-of select="gl:title"/></title></head>
<body>
<img src="{gl:image}" align="left"/>
<h1><xsl:value-of select="gl:title"/></h1>
<p><xsl:value-of select="gl:date"/></p>
<p><xsl:value-of select="gl:description"/></p>
<p>
<xsl:if test="preceding-sibling::gl:photo">
<a href="photo-{position() - 1}.html">Previous</a>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:if test="following-sibling::gl:photo">
<a href="photo-{position() + 1}.html">Next</a>
</xsl:if>
</p>
</body>
</html>
</xalan:write>
</xsl:template>
</xsl:stylesheet>
|
源代码(请参阅
参考资料)包括文档、样式表以及用于测试该代码的一个小型的 Java 应用程序。请确保您是在 JDK 1.4 上运行该示例。
xalan:redirect 要求处理器将元素内容保存到一个独立的文件中。该文件的名称由
select 属性给出。在本示例中,样式表通过将照片编号(更精确地说是照片的位置)添加到
photo-x ,生成文件名。这些文件被命名为
photo-1.html 、
photo-2.html 、
photo-3.html 和
photo-4.html 。
遗憾的是,
xalan:redirect 并不属于标准,因而其它处理器不能识别它。
xalan:redirect 是作为一种扩展来实现的。要声明该扩展,您首先必须为
org.apache.xalan.xslt.extensions.Redirect URI 声明名称空间。诚然,
org.apache.xalan.xslt.extensions.Redirect 不是一个有效的 URI,但 Xalan 仍能识别它。之后,您要通过
extension-element-prefixes 属性将名称空间声明为一个扩展。不管是名称空间声明还是
extension-element-prefixes 属性都必须出现在
xsl:stylesheet 元素中。
其它处理器怎样?
Xalan 是一个不错的处理器,但如果您想用别的处理器又该如何呢?暂时,您只能钻研您喜爱的 XSLT 处理器的相关文档,找出相应等价的扩展。就我所知,每一个 XSLT 处理器至少提供一个扩展来支持多个输出文档。
举例来说,如果您想用 Michael Kay 优秀的 Saxon 处理器,那么您应该重写
gl:photo 模板,以便用
saxon:output 代替
xalan:redirect 。因为
saxon:output 和
xalan:redirect 非常相似,所以所做的改动很小。清单 3 是
清单 2的 Saxon 版本(注意我们为扩展使用了 Saxon 定义的名称空间)。
清单 3. saxon.xsl - 用于 Saxon 的样式表
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gl="http://ananas.org/2003/tips/gallery"
xmlns:saxon="http://icl.com/saxon"
extension-element-prefixes="saxon">
<xsl:output method="html"/>
<xsl:template match="gl:gallery">
<html>
<head><title><xsl:value-of select="gl:title"/></title></head>
<body>
<h1><xsl:value-of select="gl:title"/></h1>
<p>The photos are <a href="photo-1.html">here</a>.</p>
</body>
</html>
<xsl:apply-templates select="gl:photo"/>
</xsl:template>
<xsl:template match="gl:photo">
<saxon:output href="photo-{position()}.html">
<html>
<head><title><xsl:value-of select="gl:title"/></title></head>
<body>
<img src="{gl:image}" align="left"/>
<h1><xsl:value-of select="gl:title"/></h1>
<p><xsl:value-of select="gl:date"/></p>
<p><xsl:value-of select="gl:description"/></p>
<p>
<xsl:if test="preceding-sibling::gl:photo">
<a href="photo-{position() - 1}.html">Previous</a>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:if test="following-sibling::gl:photo">
<a href="photo-{position() + 1}.html">Next</a>
</xsl:if>
</p>
</body>
</html>
</saxon:output>
</xsl:template>
</xsl:stylesheet>
|
目前正在由 W3C 开发的 XSLT 2.0 定义了一个标准指令来生成多个输出。实际上,该指令和
xalan:redirect 或
saxon:output 非常相似,但有了标准名称。在 2003 年 5 月 2 日,XSLT 2.0 的草案(撰写本文时的最新可用草案)中,该指令名为
xsl:result-document 。清单 4 演示了它的用法。请注意,正如 version 属性表明的那样,这是一个 XSLT 2.0 样式表。
清单 4. xsl2.xsl - 利用新标准指令 xsl:result-document 的 XSLT 2.0 样式表
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gl="http://ananas.org/2003/tips/gallery">
<xsl:output method="html"/>
<xsl:template match="gl:gallery">
<xsl:apply-templates select="gl:photo"/>
<html>
<head><title><xsl:value-of select="gl:title"/></title></head>
<body>
<h1><xsl:value-of select="gl:title"/></h1>
<p>The photos are <a href="photo-0.html">here</a>.</p>
</body>
</html>
</xsl:template>
<xsl:template match="gl:photo">
<xsl:result-document href="photo-{position()}.html">
<html>
<head><title><xsl:value-of select="gl:title"/></title></head>
<body>
<img src="{gl:image}" align="left"/>
<h1><xsl:value-of select="gl:title"/></h1>
<p><xsl:value-of select="gl:date"/></p>
<p><xsl:value-of select="gl:description"/></p>
<p>
<xsl:if test="preceding-sibling::gl:photo">
<a href="photo-{position() - 1}.html">Previous</a>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:if test="following-sibling::gl:photo">
<a href="photo-{position() + 1}.html">Next</a>
</xsl:if>
</p>
</body>
</html>
</xsl:result-document>
</xsl:template>
</xsl:stylesheet>
|

 |

|
结束语
当我们发布一个网站时,将一个 XML 文档分解成几个较小的文件对于方便下载和提高效率往往会有所帮助。在使用框架时这个方法也很有用。不过,多个输出并不仅限于发布。在电子商务项目中,我也用这种技术来分解大型数据库的导出,得到较小的,更便于管理的文件。
参考资料
关于作者  | 
|  | Benoit Marchal 是一名比利时顾问。他是 XML by Example 及其他一些 XML 书籍的作者。Benoit Marchal 能够在 XML 项目方面向您提供帮助。请通过 bmarchal@pineapplesoft.com 与 Benoit 联系。 |
对本文的评价
|