如我在本系列的 第 1 部分 所述,在文档中使用 XML(及其前身 Standard Generalized Markup Language (SGML))可以将文档的内容(文档包含的实际文字和数据)与其表示(内容以特定输出格式显示的方式,或在特定输出设备上显示的方式)分离开来。这种分离使您能更多地关注文档的内容,而不是信息最终是如何使用和表示的。第 2 部分 讨论了将 XML 文档组织成更小的信息单元(通常称为块)的各种机制,以创建更大的文档,或通过串联不同的信息块或以不同的顺序串联同样的信息块创建面向不同受众的文档。
XML 作为与软件无关的文档环境加速了信息重用,随之而来的是定制信息需求的增长,以便满足不同的受众或输出格式。这种重用通常称为单一来源文档,因为一组输入文件即可满足多个受众或输出格式的需求。某些单一来源需求是由工具自动处理的,可以产生多种格式的输出。例如,为包含到外部资源链接的 DocBook XML
文档生成 PDF 格式的输出(使用
<ulink> 元素),可以将超链接嵌入
该信息并在输出中嵌入实际 URL,而生成来自同一 XML 文档的 HTML 格式的输出只需在该 HTML
输出中嵌入链接即可。
以不同的方式将单一元素转换为不同的输出格式是向处理单一来源文档的正确方向迈出的第一步,但它不支持超越文档的表示要求来定制文档的内容。能够根据其目标输出格式定制文档实际内容是现代文档的一个很常见的要求。幸运的是,这可以通过结合使用预处理和灵活的文档格式(如 DocBook XML)设计功能轻松地实现。
XML 元素使用称为属性 的名称-值对提供关于元素实例的信息。通常,属性可以惟一地标识 XML 文档中同一元素的不同实例或标识一个元素的不同实例的特殊化行为。属性包含在元素范围内并具有以下形式:
<attribute_name="value"> |
举个例子,最常见的
XML 元素属性是 id 属性,它可以惟一地标识
XML 元素的给定实例,如下例所示:
<section id="introduction"> |
在 XML 文档中,id 属性通过
<xref> 和
<link> 之类的元素支持交叉引用。以下是对上例中使用的
<section> 元素引用的示例:
<xref linkend="introduction"/> <link linkend="introduction">introduction to this document</link> |
标识元素的专门化行为的属性示例是 <emphasis> 元素的 role 属性:
<emphasis role="bold">example</emphasis> |
包括在 <emphasis> 元素之内的内容通常以斜体呈现,采用 PDF 或 HTML 之类的表示格式。指定 role="bold" 属性会将该元素的呈现从斜体改为粗体。
过去,标记语言倾向于支持以某种程度定制所支持的元素集的文档生成。创建可以以特殊方式在给定工具内使用的定制元素是最初的通用标记语言(Generalized Markup Language,GML)的功能(GML 是 SGML 的前身,有关更多信息,请参见 参考资料)。从两者的缩略词可以看出,GML 和 SGML 的主要不同在于出现在给定文档类型定义中的元素集合是否标准化。
即使 SGML 以及稍后的 XML 文档类型设计用于使用标准的、预定义的元素集合,还是可以通过引用定义这些元素的外部资源来添加自定义元素定义。但是将定制元素定义添加到现有 DTD 或模式通常不是一个好主意,原因很多,最主要是因为:
- 使用非标准元素的文档不符合标准的文档类型,如 DocBook 或 DITA。
- 扩展 DTD 或模式会给与其他使用默认 DTD 或模式的机构或公司交流文档带来不便。即使也会交流非标准元素的定义,但是成功的交流文档还需要更多的定制工作。
- 设计用于处理标准文档类型的工具也要被扩展以支持新的元素。尽管对于开源工具而言,这个操作相对较容易,但是对于私有的闭源工具而言,这种扩展可能无法进行。
本文介绍了使用预处理步骤消除不适用于特定受众或输出格式的条件化文本。某些图形 XML 文档工具提供了类似的解决方案,使您能设置在生成输出过程中使用的变量。本文重点介绍了更通用的预处理方法,可以为大量开源 XML 文档工具所使用,而不是仅用于特定文档工具支持的机制。
如您所料,不同的 XML 元素根据具体元素的类型和用途有不同的属性。但是,XML 文档模式和 DTD,如 DocBook 文档类型(本系列文章关注的重点),还定义了常用的属性集合(有关更多信息,请参见 参考资料),它们可以用于任何 XML 元素,通常用于标识给定元素的表示或目标的信息。这些属性的常用示例包括:
arch:旨在标识某元素的给定实例内的内容应用于哪个计算机系统或处理器架构audience:旨在标识某元素的给定实例内的内容应用于哪些受众condition:用于局部、特定于应用程序的内容定制os:旨在标识某元素的给定实例内的内容应用于哪个计算机操作系统revision:旨在标识某元素的给定实例内的内容应用于哪个特定的软件或文档修订vendor:旨在标识某元素的给定实例内的内容应用于哪个硬件或软件供应商
为这些属性中的任何一个定义一组有效值,然后实现预处理器,放弃该属性中具有其他值的元素,可以轻松的条件化 您的文档。对哪些属性进行定制完全由您决定,但是通常会根据哪些属性最适合您要条件化的内容的类型以及定制原因来确定。本文使用通用的 condition 属性作为特定于给定输出/表示格式的通用条件化的一个例子。
下面两节介绍如何使用属性(比如以上属性)有条件地标识特定元素或元素内的内容片段。
上一节 介绍的常用属性提供了一种简单的方式标识您要关联到特定受众或输出格式的文档部分。本节使用 condition 属性作为示例,当然也可以使用上一节介绍的任何常用元素,只要您的站点还没有使用过。
条件化内容的常见类型是出现在文档中的文本,它通常采用不同的表示格式进行了格式化。例如,在线使用的文档通常在每一节的末尾包括到下一节的链接,以便让读者可以轻松导航。这些链接大概是这样的:
<para> To proceed to the next section of this tutorial, click <link linkend="link-to-next-section">here</link>. </para> |
尽管在在线文档中很有用,但是当同一个文档用作
Adobe® PostScript® 或 PDF 格式的文档时,这些信息看起来就很多余而且让人糊涂了。要标识文档的这一部分,以表示它仅用于文档的在线展示,可以将
condition="online"
属性添加到 <para> 元素,如下例所示:
<para condition="online"> To proceed to the next section of this tutorial, click <link linkend="link-to-next-section">here</link>. </para> |
如果只将 上一节 介绍的属性添加到给定元素就可以让您的文档工具正确工作就好了,但是这种情况很少见。要利用这种条件化,您需要编写一个小的 XSL 脚本,在将某一部分格式化成其他表示格式时放弃该部分。清单 1
展示了一个 XSL 脚本示例,该脚本放弃了具有
condition 属性且该属性的值不是
print 的内容元素。
清单 1. 打印输出的预处理脚本
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xi="http://www.w3.org/2003/XInclude">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:preserve-space elements="*"/>
<xsl:template match="*|@*">
<xsl:variable name="_element">
<xsl:value-of select="name()" />
</xsl:variable>
<xsl:variable name="_condition">
<xsl:value-of select="@condition" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$_condition = ''">
<xsl:copy>
<xsl:apply-templates select="@* | * | text() | comment()" />
</xsl:copy>
</xsl:when>
<xsl:when test="$_condition = 'print'">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:message> Skipping <xsl:value-of select="$_element"/></xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
|
注:该脚本的编写主要考虑了可读性而不是美观。您当然可以简化它。
使用上一个示例进行配置,清单 1
中的脚本放弃了其
condition 属性将其标识为用于其他输出格式的元素。这一定位移除了前面
“仅在线” 示例中显示的内容,因为其 condition
属性具有值 online。脚本的第一部分处理每个元素并将元素名和
condition 属性的任何值指定给相关变量。脚本的其他部分根据
condition 属性的值有条件地输出内容。如果没有出现 condition
属性或该属性的值为 print,则包含的元素将被复制到脚本的输出。如果
condition 属性具有其他值,则禁止相关元素在输出中出现,处理继续进行。
要轻松地将脚本(比如这个脚本)整合到文档生成过程,将它添加到生成 PDF 或 PostScript 文档的 Makefile 部分。为此,可以使用这个脚本来处理输入文档,将输出重定向到一个文件,然后对该文件进行适当地格式化。
清单 2 显示了同样的 XSL 脚本,该脚本经过定制,放弃了具有
condition 属性且该属性的值不是 online 的内容元素。将该脚本添加到用于在线文档的 Makefile 目标中,使您能包括那些在 上一个代码示例 中被标记为仅在线的元素。
清单 2. 在线输出的预处理脚本
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xi="http://www.w3.org/2003/XInclude">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:preserve-space elements="*"/>
<xsl:template match="*|@*">
<xsl:variable name="_element">
<xsl:value-of select="name()" />
</xsl:variable>
<xsl:variable name="_condition">
<xsl:value-of select="@condition" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$_condition = ''">
<xsl:copy>
<xsl:apply-templates select="@* | * | text() | comment()" />
</xsl:copy>
</xsl:when>
<xsl:when test="$_condition = 'online'">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:message> Skipping <xsl:value-of select="$_element"/></xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
|
比较 清单 1 和 清单 2 中的 XSL 代码,会发现两者只有一个单词不同,通过对 XSL 中及 xsltproc 中提供的参数(stringparam)的支持,两个代码都可以进行简化。我把这个作为练习留给大家,因为这些示例的目标是可读性,而不是如何编写美观的 XSL 的速修课程。
上一节 中的示例很适合用于根据属性值包括或排除整个元素。然而,大部分 XML 文档还需要条件化另一个元素中的文本的某些部分。最常见的是,可能需要自定义某个段落中的特定内容。这可以通过两种方式完成:
- 条件化出现在段落内的元素
- 使用
<phrase>元素条件化某一段落内的文本部分
要条件化出现在段落内的元素,将用于条件化的属性和适当的值添加到段落内适当的元素。例如,清单 3 中显示的文档片段提供了在段落内条件化的
<mediaobject> 元素(采用可读性格式)。
清单 3. 条件化的 mediaobject 元素
<para>
To create a new file, click the <emphasis
role="bold">Add</emphasis> icon
(<mediaobject>
<imageobject audience="online">
<imagedata fileref="add.gif"/>
</imageobject>
<imageobject audience="print">
<imagedata fileref="../../images/add.gif" />
</imageobject>
</mediaobject>)
beside the <emphasis role="bold">New File</emphasis> menu entry.
</para>
|
尽管主要用在此处的示例中,条件化
<mediaobject> 元素使您能以不同的表示格式指定不同的路径来定位图形。在本例中,在线文档在给定 Web 服务器或虚拟 Web 服务器上的集中存放图形的位置查找图形,而打印文档出于格式化目的,将图形定位在特定目录中。
使用 <phrase> 元素标识条件化的文档部分更加有趣。通过
<phrase> 元素,您可以标识比段落更小的特定文本范围,很适合用于文档条件化。清单 4(采用可读性格式)展示了条件化单个词使文档更特定于某个表示格式的示例。
清单 4. 条件化单个词
<p>
See the
<link linkend="target-id">
introduction to this
<phrase audience="online">portion</phrase>
<phrase audience="print">chapter</phrase>
of the documentation
</link>
for more information.
</p>
|
XML 的威力和灵活性、现有标准的集合以及用于处理和转换 XML 文档的丰富工具集为创建和维护文档提供了强大的环境。本文介绍的属性和技巧使您能轻松创建包含面向特定受众、计算机系统或表示格式的不同内容的条件化文档。如果在文档生成过程中添加简单的预处理阶段或设置变量,您就可以创建和维护单一来源文档,生成专门化的输出。此功能为传统文档问题提供了极其有用的和灵活的解决方案,可以简化文档开发和维护,同时最好地满足该文档的特定用户群的需求。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 用于打印输出的样例 XSL 脚本 | conditional-print.zip | 1KB | HTTP |
| 用于在线输出的样例 XSL 脚本 | conditional-online.zip | 1KB | HTTP |
学习
- 在 XML 内生成文档并重用信息,第 1 部分:利用 XML 发布文档(William von Hagen,developerWorks,2009 年 3 月):在任何
UNIX® 或 Linux™ 系统上轻松地组建 XML 发布系统。将免费的开源包与 XML 结合起来,从而实现高效的信息重用,以及从一组源文档生成多种格式的文档。
- 在 XML 内生成文档并重用信息,第 2 部分: 在 XML 文档中重用信息(William von Hagen,developerWorks,2009 年 3 月):结构化 XML 文档和片段以进行重用。在本系列的第 2 部分中,通过 XInclude 包括外部文档部分,通过 XPointer 将小的文档片段包括到较大的文档中。
- DocBook: The Definitive Guide:获得有关 SGML 和 XML 内的 DocBook 标记的全部信息。本书的全部内容可以在线获得,但是如果您想要用 DocBook 进行正式的发布,那么此书非常值得您去购买。
- DocBook XSL: The Complete Guide:阅读关于 DocBook 样式表和发布的权威信息。本书的全部内容可以在线获得,但是如果您想要用 DocBook 进行正式的发布,那么此书非常值得您去购买。
- 常用属性:查看常用 DocBook XML 元素集合列表。
- GML:了解维基百科上关于 Generalized Markup Language 的条目。
- IBM XML 认证:了解如何成为 IBM 认证的 XML 和相关技术开发人员。
- XML 技术库:访问 developerWorks XML 专区,获得广泛的技术文章和技巧、教程、标准和 IBM 红皮书。
- developerWorks 技术活动 和 网络广播:随时关注技术的最新进展。
- 技术书店:浏览关于这些和其他技术主题的图书。
- developerWorks
podcasts:收听针对软件开发人员的有趣访谈和讨论。
获得产品和技术
- DocBook DTD:下载最新的版本,开始行动。
- DocBook XSL 样式表:下载最新版本。(访问 DocBook Project 站点 > 文件发布并选择 docbook-xsl)。
- nXML 模式:如果计划在 Emacs 文本编辑器中编辑 XML 文档,(您应该)下载最新版本的 XML 模式的 Emacs。
- IBM 产品评估版:下载或 在线试用 IBM SOA Sandbox,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
- XML 专区讨论论坛:参与几个与 XML 有关的讨论之一。
- developerWorks XML 专区:分享您的想法:阅读本文后,在论坛中发表评论和看法。XML 专区的编辑会主持这个论坛,欢迎您的评论。
- developerWorks 博客:查看这些博客并加入 developerWorks 社区。