级别: 初级 David Mertz,博士 (mertz@gnosis.cx), 转换专家, Gnosis Software, Inc.
2000 年 11 月 01 日 通过使用 DocBook 示例,这位无畏的专栏作家演示了如何通过 XSLT(可扩展样式表语言转换)将 XML 文档转换成 HTML。David Mertz 一共讨论了四种可转换 XML 文档的可选方式,并和我们分享了他试验某些开放源码工具的经历。样本代码包括了 XSLT 文档片段、以 XSLT 表示的,简单 DocBook 章节的有效 HTML 输出器代码,以及一个简要的 XSLT 循环示例。
欢迎来到 XML转换的世界!我想您可能不会一帆风顺:标准正在接合并进行修订、工具还不成熟并经常有错误、实现不一致,并且选择非常混乱。但不要惊慌。我可以为您指引至少一条走出迷宫的途径。而且随着时间的推移,情况必定会有所改善,尽管改善的速度往往不如我们所愿。
首要事情
最后两个“XML 问题”专栏描述了我将学术文章转换成 XML的项目,具体来说是转换到 DocBook DTD的项目。那些文章提供了一个编写您自己的 DocBook文档的良好起点,这就是本专栏目的所在。
在本专栏中,我们假设您已经有了一些结构良好、格式正确、有效的DocBook XML文档。首先非常好的事是拥有它们,但下一步是将它们转换成更加方便的最终用户格式:例如HTML 页面、PDF文件和印刷页面(读者实际所阅读的)等。这正是我在将部分归档作品转换成DocBook 后所面临的问题,本文提供了我自己的解决方案。
我的主要目标 -- 至少目前 -- 是到 HTML 的正确转换。但我不希望限于HTML输出。还有一些更小的目标。我希望对精确输出具有某些控制而无需执行许多操作,也无需了解许多新的语言和技术。我还希望使用免费的工具和跨平台的工具。最后,我希望将依赖性降低到最小。即使所有必需的贡献都是免费和跨平台的,大量复杂的依赖性也是个缺点。基本上,我的理想是有一个独立的可执行程序,只运行,可靠地运行,将我的DocBook 文档以我所希望的样式转换成HTML。高不可攀的梦想,但为什么不能这么想呢?
执行转换的方式
至少有四种可能的方式来将 DocBook 文档 -- 或几乎所有 XML 文档 --转换成最终用户格式。我很认真地为我的小项目考虑过所有四种方式。本专栏只详细讨论最后一种选项,但在您规划涉及到重复转换的项目时,有必要记住所有方式:
-
编写定制转换代码。最好从一种具有基本 XML方法库的编程语言(例如 SAX 和DOM)开始。但即使假设基本语法分析是个黑箱,定制代码也可以对语法分析过的元素执行所有您希望执行的操作。归根结底,这是最灵活和最强大的方式,但也往往会带来更多工作,不论是事先的还是维护的。
-
使用级联样式表和 DocBook文档。这是一种想法。最好能将排版规范完全与结构化标记分离,并只让客户机设备(例如浏览器)产生良好的输出。这有可能发生,但就目前来说,似乎支持有限-- 只在 IE 5.5、Opera 4 和某些最新的 Mozilla开发者发行版中支持。现在这不象是可以依靠最终用户为他们执行这些任务的方法。
-
使用文档样式语义和规范语言 (DSSSL)来指定到目标格式的转换。从好的方面说,已经存在一些 DocBook(以及其它格式)DSSSL 样式表。DSSSL基本上是一门需要学习的全新编程语言,而且是类似于 Lisp的功能性语言。为利用 DSSSL,需要从 Jade 或 OpenJade开始,但这两种工具都很复杂,以至于许多人都必须为它们编写封装器(例如SGML-tools Lite)。为了获得有用的系统 --虽然有报告说是个非常好的有效系统 --您确实需要满足所有种类的系统依赖性,并安装所有种类的工具和库。对于某些有良好意愿,然而可能没有投入足够精力的尝试,我没能让与Jade相关的工具在我的系统上顺利地发挥作用。很明显,有其他许多人每天都在使用这些系统,所以稍微多做一些工作一定能把事情办得井井有条。(如果您能指点一个快速、简单、多合一的DSSSL处理器,请告诉我。我非常希望尝试一下。)然而除了安装困难外,DSSSL感觉上象是来自与 XML技术不同的传统和思考方法,相反,最后一个方法基本上是纯XML,并来自正式(有效)的 W3C 规范。
-
使用可扩展样式表语言转换 (XSLT)。从某种意义上说,XSLT实际上是 XML 文档类的一个规范。即,XSLT 样式表本身是格式正确的 XML文档,并带有一些专门的内容,可以让您“模板化”您所期望的输出格式(继续阅读它的含义)。有许多工具至少从名义上支持XSLT:我的预感是,这确实是 XML 转换的技术方向 --因为,或者尽管,相对于 W3C 的“正式”身份。XSLT可以指定到任何目标格式的转换。但给我的一般感觉是,大多数开发者发现,在目标格式是另一种XSLT 格式(例如 XHTML)时,使用 XML 更容易。

 |

|
选择 XSLT 工具
参考资料部分包含了到许多 XSLT工具描述的链接。我尝试过其中的许多,但发现 Sablotron最合我口味。它是自由软件(GNU)。它是多平台的。它有许多独立的可执行程序,这些程序可以简单地从命令行运行。最重要的是,它看起来能正确工作,至少对于我的简单测试案例来说如此。
XSLT.com 列出的许多其它 XSLT工具也是自由软件。不过,它们中的大多数是 Java程序,也要依赖于各种额外的 Java 库。用户似乎给了一些 Java工具正面评价,因此这些工具对您来说可能是很好的选择。我选择Sablotron 是因为编译过的 C 速度更快,安装和使用都很简单。
Norman Walsh 为 DocBook 创建了一系列
完整的XSLT样式表。不幸的是,将它们用在样式表时,Sablotron 崩溃了,并且 XMLSpy 不能匹配有效 DocBook 文档中的任何东西。这很可能是工具而不是Walsh样式表的一个限制。使用其它工具可能运气会好些。这个问题仍然给了我们开发定制(不很完整)XSLT样式表的机会,无论如何这是我真正希望的(为了演示技术)。
Sablotron 的使用非常简单。基本用法是:
清单 1: Sablotron 的基本用法
X:\mydocs> x:\sabl\bin\sabcmd mystyle.xsl mydoc.xml
mydoc.html
|
它说的是:使用
mystyle.xsl 中的规则来将
mydoc.xml 转换成
mydoc.html ,如果希望,也可以使用管道和重定向。设置Sablotron和解开它的档案一样容易(它还提供了能从程序调用的库,但最好从命令行实用程序中使用。)因此可以按环境需要调整路径和文件名。
编写 XSLT 规范
有关 XSLT 的实质,请阅读 W3C 的官方建议书(请参阅
参考资料部分)。本文旨在提供让它工作的更多非正式细节。
“
XML 问题 #3”和“
XML 问题 #4”中介绍的特定 DocBook 文档 (
chap5.xml )是一
章。示例使用了章节中所有可能的 DocBook标记中相当小的一部分。所以目前来说,我们实际上所需要的就是
chapter.xsl 文件,它将对
chap5.xml 中实际使用的每个标记做些有用的事。这是适当的开始,但易于构建是因为XSLT 具有开放和可扩展的本质。让我们看一下。
从
chapter.xsl 的骨架开始 -- “如何将 DocBook章节转换成 HTML”模板:
清单 2: 骨架 XSLT 文档 (empty.xls)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict">
<xsl:output method="html" indent="yes" encoding="UTF-8"/>
</xsl:stylesheet>
|
可以看到,
chapter.xsl 是个格式正确的 XML文件。您还将注意到,模式
<xsl:*> 是 XSLT文档中许多标记的名称。实际上,所有属于指令的标记都象这样。在转换到类似XML 格式(例如HTML)的过程中,将看到各种其它标记。这些其它标记属于目标格式,只在
<xsl:*> 元素中出现。
基本上,应该确切使用如上指出的名称空间属性(
xmlns:xsl 和
xmlns )。可能还希望保留输出行;尽管可以使用
xml 或
text 方法。
上面的 XSLT文件作为处理模板使用得非常好。但这可能不是您所需要的。可以假设因为缺少输出规范而没有任何输出。这不完全正确:它仍然捕捉所有文本节点,并提供简单ASCII版本的章节(使用上述样式表)。如果
确实希望没有一点输出,需要有类似清单3 中的一个 XSLT 文档:
清单 3: Null 输出 XSLT 文档 (null.xls)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict">
<xsl:output method="html" indent="yes" encoding="UTF-8"/>
<xsl:template match="*">
</xsl:template>
</xsl:stylesheet>
|
null输出器使我们的转换更有用。真正的样式表实际上只描述了一系列要尝试匹配的模式,模板在每个提供输出内容模板的
<xsl:template> 元素内部。如示例所示,"*"可以与任何模式匹配。我们的示例碰巧没有在模板中
做任何事,但它仍然努力与源XML/DocBook 文档中可能出现的任何元素匹配。
通过下降进行匹配
XSLT的威力主要在于它们扩展匹配功能的能力。一旦匹配了一个元素,XSLT就将匹配功能扩展到该元素的子元素。通过在 null输出器上进行扩展,让我们创建一个有一定意义的样式表。允许下降到子元素的重要标记是
<xsl:apply-templates> 。一般来说,每个模板都在其主体中包括这个标记:
清单 4: 最小章节 XSLT 文档 (minimal.xls)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict">
<xsl:output method="html" indent="yes" encoding="UTF-8"/>
<xsl:template match="chapter">
----- Start of Chapter -----
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*">
##### Unmatched Element in Source #####
</xsl:template>
</xsl:stylesheet>
|
使用该样式表和 DocBook 章节运行 XSLT处理器时,得到的结果类似于:
清单 5: 使用样式表和 DocBook章节的 XSLT 处理器
----- Start of Chapter -----
##### Unmatched Element in Source #####
##### Unmatched Element in Source #####
##### Unmatched Element in Source #####
|
该输出不那么有用,但它让我们看到样式表做了些什么。章节的根元素是
<chapter> 标记。样式表匹配
<chapter> 标记,并打印 " - - - - - Start ofChapter - - - - - "。各种子代出现在
<chapter> 元素中。每一这样的子代都称为非章节内容,因此将匹配 "*" 模板。
为开发自己的 XSLT样式表,提供类似上面非匹配元素的某些明显标记,可以让您很快看到需要开发哪些模板。清单6 显示了带有一些真实模板的版本:
清单 6: 有效的 HTML 输出器 XSLT文档
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict">
<xsl:output method="html" indent="yes" encoding="UTF-8"/>
<xsl:template match="chapter">
<html>
<head>
<title>
<xsl:value-of select="title"/>
</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="chapter/title">
<hr></hr>
<h1><xsl:apply-templates/></h1>
</xsl:template>
<xsl:template match="para">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="*">
##### Unmatched Element in Source #####
</xsl:template>
</xsl:stylesheet>
|
该 HTML 输出器显示了 XSLT 样式表的某些实际特性。
chapter模板匹配对希望产生的 HTML 文档进行排版。模板匹配内部的 HTML标记没有什么特别;您放在那里的所有文本都将出现在输出中。在 HTML
<title> 元素中,使用
<xsl:value-of> 指令将
<chapter> 内部必需的
title子元素插入到DocBook。在 HTML
<body> 元素中,将控制传递给其它模板(大概 DocBook 中极少的一部分)。
chapter后的下一个模板是
chapter/title。这意味着匹配
<title> 元素,但只有在它直接出现在
<chapter> 内部时。如果希望,可以只匹配
title,从而指定源文档中每个
<title> 元素的输出格式。但我希望将章节标题格式化成不同于
sect1的标题、
sect2的标题等。使用示例中的
para(但从不真正匹配,因为
para只能出现在还不匹配的标记内部)。为进行准确的衡量,模板仍然匹配"*",因此可以在检查输出时看到样式表不完整。
重复的子代
通过下降匹配模板不是 XSLT唯一的技巧。还可以执行有条件的输出、排序、取出源属性,然后在子代上循环。就目前来说,只需要看一下清单7 中简单的循环示例:
清单 7: 在子元素上循环的 XSLT模板
<xsl:template match="simplelist">
<ul>
<xsl:for-each select="member">
<li><xsl:apply-templates/></li>
</xsl:for-each>
</ul>
</xsl:template>
|
不用下降到
simplelist中的每个子元素,我们只假设子元素都是
<member> 元素。
<xsl:for-each> 的工作方式与嵌套模板非常相似,而且与编程语言循环构造也非常相似。
<xsl:for-each> 元素的内容将出现在匹配
select属性的每个子元素的输出中。在循环中,当前
<member> 元素的内容成为下降到我们在循环中找到的
<xsl:apply-templates/> 标记的活动节点。就是说,列表中的每样事物在其内部都有进一步的标记,我们将这些元素的格式传递给它们相应的模板(对于文本节点,它们就是文字格式的输出)。
未来
前面的资料只揭开了 XSLT的面纱。但它应该为您提供了使用样式表和转换的一些认识。
参考资料部分提供了进一步阅读相关问题的许多来源。特别是通过阅读本文档案文件中更完整的XML 和 XSLT 示例能从中获益。请别走开,本专栏将以各种方式再回来介绍XSLT。
参考资料
关于作者
对本文的评价
|