使用 XSLT 转换 DocBook 文档

  • Post author:
  • Post category:其他


David Mertz,博士

转换专家, Gnosis Software, Inc.

2000 年 11 月



内容:







首要事情



方法



选择 XSLT 工具



编写 XSLT 规范



通过世系进行匹配



重复的子代



未来



参考资料



关于作者



通过使用


DocBook


示例,David Mertz 演示了如何通过 XSLT (可扩展样式表语言转换)将 XML 文档转换成 HTML。这位无畏的专栏作家一共讨论了四种转换 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 崩溃了,并且 XML Spy 不能匹配有效


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 of Chapter – – – – – “。各种子代出现在

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



参考资料



关于作者

David Mertz
David Mertz 一定在他的其它一篇文章中错放了他的 MacGuffin。它将很快出现。可以通过


mertz@gnosis.cx


与 David 联系;在


http://gnosis.cx/publish/


上详细介绍了他的生活。非常欢迎对过去的、这一篇或将来的专栏文章提出意见和建议。