使用 XSLT 转换 XML

内容与呈现的分离是 XML 的一个关键设计特点。XML 文档的结构旨在反映和阐明内容自身各个方面之间的重要关系,不受需要提供任何关于数据最终如何呈现的指示的阻碍。随着越来越多的数据传输自动化并在通过网络连接的高度异构机器之间进行,这种智能结构变得尤为重要。

然而,最终存储在 XML 文档中的大部分内容都需要呈现给人类读者。由于浏览器提供了熟悉且高度灵活的界面,它是交付此类 XML 内容呈现版本的理想机制。Firefox 从零开始,利用各种 XML 技术构建,自身集成了处理原始 XML 文档和用于样式化及布局以供 HTML 显示的专用样式表所需的所有机制,通过客户端处理减少了服务器负载。

目前,Gecko(Firefox 背后的布局引擎)支持两种形式的 XML 样式表。对于外观的基本控制——字体、颜色、位置等——Gecko 使用 CSS

我们在此关注 Gecko 支持的第二种样式表:XSLT 样式表。XSLT 代表“可扩展样式表语言/转换”(eXtensible Stylesheet Language/Transform),这个名称非常贴切。XSLT 允许样式表作者以两种重要方式转换主 XML 文档:操作和排序内容,如果需要,甚至可以进行整体重排序;以及将内容转换为不同的格式(在 Firefox 的情况下,重点是在运行时将其转换为 HTML,然后浏览器可以显示它)。

什么是 XSLT?

可扩展样式表语言/转换(eXtensible Stylesheet Language/Transform)是一种非常强大的语言,对其进行完整讨论超出了本文的范围,但对一些基本概念的简要讨论将有助于理解后续的 Netscape 功能描述。

XSLT 样式表是一个 XML 文档。与 CSS 有其自己的专用语法不同,XSLT 样式表是一个 XML 文档,必须符合所有 XML 规则,包括格式良好性。因此,转换的模型是使用一个 XML 文档来转换另一个 XML 文档。

XSLT 样式表通过包含标准 XSLT 标题来标记。XSLT 样式表中最外层的元素必须是 <xsl:stylesheet> 元素(一个可接受的替代方案是 <xsl:transform> 元素)。此元素将至少包含一个命名空间声明和强制的版本属性。也可以包含其他命名空间和三个可选属性。

XSLT 命名空间

XSLT 的强制命名空间是 "http://www.w3.org/1999/XSL/Transform"。命名空间在 XML 中引起了相当多的困惑。尽管命名空间看起来常常是 URI,但它们实际上并不引用该地址处的资源。相反,它们是一种为已知元素集指定唯一标识符的方式。字符串 "http://www.w3.org/1999/XSL/Transform" 是一个常量,它指定那些被标记的元素属于 W3C 在 1999 年 XSLT 推荐标准中指定的标签集。样式表中偶尔出现的另一个字符串 "http://www.w3.org/TR/WD-xsl" 表示符合 W3C 文档的早期工作草案(因此有 WD)。后一个命名空间与 W3C 最终采用的命名空间不兼容,并且不受 Netscape 支持。

由于重复键入 "http://www.w3.org/1999/XSL/Transform" 会很繁琐并导致标记难以阅读,因此有一种标准机制可以在样式表标题中为命名空间分配一个简写名称。因此,开放样式表元素的完整示例可能如下所示。

xml
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

xmlns 伪属性将简写名称 xsl 映射到整个命名空间,以便在后续文档中使用。因此,上面的样式表元素前缀为 xsl:。尽管 xsl 是约定俗成的简写名称(称为前缀),但它并非强制性,并且完全可以选择不同的名称。本文中的示例都假设使用 xsl 前缀。

XSLT 如何处理 XML 树

XSLT 转换引擎(称为处理器)不直接作用于文档。在转换发生之前,主 XML 文档和样式表文档必须经过解析器处理,解析器会在内存中创建文档结构的抽象表示。这种表示被称为树,是处理器实际操作的对象。树是一种抽象数据类型,一个概念模型,可以根据解析器和处理器以各种方式实现。Netscape 使用类似于 W3C DOM 的结构作为其树结构,但其他结构也是可能的。唯一的要求涉及树中对象的布局、它们的属性及其关系。

这棵树由节点的分层框架组成。它可以由七种不同类型的节点构成:单个根节点、元素节点、文本节点、属性节点、注释节点、处理指令节点和命名空间节点。

树的顶部是根节点。根节点不对应于 XML 文档的任何单个部分:它代表整个文档。根节点下方是其子节点,可以是元素、注释、处理指令等。其中一些子节点也可能有子节点。这可以持续多个层级。关于哪些类型的节点可以出现在哪里存在某些限制:例如,文本节点不能有子节点。

处理器动作的结果也是一棵树。Netscape 使用这棵树在浏览器窗口中渲染内容。

XPath 和节点选择

本质上,XSLT 样式表是一组规则,称为模板,它们声明任何匹配此特定模式的节点都应以这种特定方式操作,并最终位于结果树中的这个特定位置。如何实现这一点则由处理器决定。由于样式表的执行顺序无法保证,XSLT 不支持任何产生副作用的功能。在这方面,它类似于 Lisp 或 Scheme。

转换取决于处理器能够在树上精确定位单个节点。为了方便这一点,W3C 决定使用一种单独的语言 XPath,它在 XSLT 上下文之外也有用途。顾名思义,XPath 定义了处理器必须遍历树以到达所需节点的“路径”。这条路径由待评估的 XPath 特定表达式组成,这些表达式可能包含多个要匹配的条件、关联节点的方式以及/或指示树内的方向性。XPath 最常用于 XSLT 的部分的更完整描述见参考部分。

模板匹配中潜在的冲突通过使用一组级联的优先级规则来解决。一般来说,更具体的模板规则优先于不那么具体的模板规则,并且,在其他条件相同的情况下,文档中稍后出现的模板规则优先于早期出现的模板规则。

样式表可以通过处理指令附加到 XML 文档。要指示应使用哪个 XSLT 样式表来处理特定的 XML 文档,请在 XML 文档本身中包含处理指令。例如,如果样式表名为 inventory.xsl 并且与 XML 文档位于同一目录中,则 XML 文档中的处理指令将如下所示

xml
<?xml-stylesheet type="text/xml" href="inventory.xsl"?>

这必须放在 XML 文档的序言部分。

基本示例

第一个例子演示了在浏览器中设置 XSLT 转换的基础知识。该例子接受一个包含文章信息(标题、作者列表和正文)的 XML 文档,并将其以人类可读的形式呈现出来。

XML 文档 (example.xml) 如下所示。

xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="example.xsl"?>
<Article>
  <Title>My Article</Title>
  <Authors>
    <Author>Mr. Foo</Author>
    <Author>Mr. Bar</Author>
  </Authors>
  <Body>This is my article text.</Body>
</Article>

XML 文件中的 ?xml-stylesheet 处理指令通过其 href 属性指定要应用的 XSLT 样式表。

此 XSL 样式表文件 (example.xsl) 如下所示

xml
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <xsl:template match="/">
    Article - <xsl:value-of select="/Article/Title"/>
    Authors: <xsl:apply-templates select="/Article/Authors/Author"/>
  </xsl:template>

  <xsl:template match="Author">
    - <xsl:value-of select="." />
  </xsl:template>

</xsl:stylesheet>

XSLT 样式表以 xsl:stylesheet 元素开头,该元素包含用于创建最终输出的所有模板。上面的示例有两个模板——一个匹配根节点,一个匹配 Author 节点。匹配根节点的模板输出文章标题,然后通过 apply-templates 处理所有匹配 Authors 节点子级的 Author 节点的模板。

尝试示例

  1. 在文件系统中创建一个目录,并在其中创建上面列出的 example.xmlexample.xsl 文件

  2. 在该目录中启动本地服务器,其中包含这些文件。这允许您像这些文件托管在互联网上一样浏览目录中的文件。

    警告: 直接从文件系统打开 XML 文件将不起作用,因为从文件系统加载样式表是 跨源请求,默认情况下将被禁止。将 XML 和样式表托管在同一个本地服务器上可确保它们具有相同的源。

  3. 从浏览器打开 example.xml

  4. 浏览器输出如下所示

    Browser Output :
    
        Article - My Article
        Authors:
        - Mr. Foo
        - Mr. Bar
    

生成 HTML

XSLT 在浏览器中的一个常见应用是将 XML 在客户端转换为 HTML。此示例将把包含文章信息的输入文档 (example2.xml) 转换为 HTML 文档。

文章的 <body> 元素现在包含 HTML 元素(<b><u> 标签)。XML 文档同时包含 HTML 元素和 XML 元素,但只需要一个命名空间,即 XML 元素的命名空间。由于没有 HTML 命名空间,并且使用 XHTML 命名空间会迫使 XSL 创建一个行为不像 HTML 文档的 XML 文档,因此 XSL 样式表中的 xsl:output 将确保结果文档被视为 HTML。对于 XML 元素,我们需要自己的命名空间 http://devedge.netscape.com/2002/de,并为其指定前缀 myNS (xmlns:myNS="http://devedge.netscape.com/2002/de")

XML 文件

xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="example2.xsl"?>
  <myNS:Article xmlns:myNS="http://devedge.netscape.com/2002/de">
    <myNS:Title>My Article</myNS:Title>
    <myNS:Authors>
      <myNS:Author company="Foopy Corp.">Mr. Foo</myNS:Author>
      <myNS:Author>Mr. Bar</myNS:Author>
    </myNS:Authors>
    <myNS:Body>
      The <b>rain</b> in <u>Spain</u> stays mainly in the plains.
    </myNS:Body>
  </myNS:Article>

使用的 XSL 样式表需要有两个命名空间——一个用于 XSLT 元素,一个用于 XML 文档中我们自己的 XML 元素。通过使用 xsl:output 元素,XSL 样式表的输出被设置为 HTML。通过将输出设置为 HTML 并且结果元素(蓝色着色)没有命名空间,这些元素将被视为 HTML 元素。

带有 2 个命名空间的 XSL 样式表

xml
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:myNS="http://devedge.netscape.com/2002/de">

  <xsl:output method="html"/>
  …
</xsl:stylesheet version="1.0">

创建了一个匹配 XML 文档根节点的模板,并用于创建 HTML 页面的基本结构。

创建基本的 HTML 文档

xml
…
<xsl:template match="/">
<html>

  <head>

    <title>
      <xsl:value-of select="/myNS:Article/myNS:Title"/>
    </title>

    <style>
      .myBox {margin:10px 155px 0 50px; border: 1px dotted #639ACE; padding:0 5px 0 5px;}
    </style>

  </head>

  <body>
    <p class="myBox">
      <span class="title">
        <xsl:value-of select="/myNS:Article/myNS:Title"/>
      </span> </br>

      Authors:   <br />
        <xsl:apply-templates select="/myNS:Article/myNS:Authors/myNS:Author"/>
    </p>

    <p class="myBox">
      <xsl:apply-templates select="//myNS:Body"/>
    </p>

  </body>

</html>
</xsl:template>
…

还需要三个 xsl:template 来完成示例。第一个 xsl:template 用于作者节点,第二个处理正文节点。第三个模板有一个通用匹配规则,将匹配任何节点和任何属性。它需要用于保留 XML 文档中的 HTML 元素,因为它匹配所有这些元素并将它们复制到转换创建的 HTML 文档中。

最后 3 个模板

xml
…
<xsl:template match="myNS:Author">
    --   <xsl:value-of select="." />

  <xsl:if test="@company">
    ::   <b>  <xsl:value-of select="@company" />  </b>
  </xsl:if>

  <br />
</xsl:template>
xml
<xsl:template match="myNS:Body">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
…

最终的 XSLT 样式表如下所示

最终的 XSLT 样式表

xml
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:myNS="http://devedge.netscape.com/2002/de">

  <xsl:output method="html" />

  <xsl:template match="/">
    <html>

      <head>

        <title>
          <xsl:value-of select="/myNS:Article/myNS:Title"/>
        </title>

        <style>
          .myBox {margin:10px 155px 0 50px; border: 1px dotted #639ACE; padding:0 5px 0 5px;}
        </style>

      </head>

      <body>
        <p class="myBox">
          <span class="title">
            <xsl:value-of select="/myNS:Article/myNS:Title"/>
          </span> <br />

          Authors:   <br />
            <xsl:apply-templates select="/myNS:Article/myNS:Authors/myNS:Author"/>
          </p>

        <p class="myBox">
          <xsl:apply-templates select="//myNS:Body"/>
        </p>

      </body>

    </html>
  </xsl:template>

  <xsl:template match="myNS:Author">
      --   <xsl:value-of select="." />

    <xsl:if test="@company">
      ::   <b>  <xsl:value-of select="@company" />  </b>
    </xsl:if>

    <br />
  </xsl:template>

  <xsl:template match="myNS:Body">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

XSLT/XPath 参考

进一步阅读

书籍

  • XSLT:程序员参考,第二版
    • 作者:Michael H. Kay
    • 页数:992 页
    • 出版商:Wrox;第二版 (2001 年 5 月 3 日)
    • ISBN: 0764543814
      • Michael Kay 是 W3C XSL 工作组成员,也是他自己的开源 XSLT 处理器 Saxon 的开发者。他也是唯一一本达到第二版的关于该主题的书的作者。这是一本非常大、排版精良且详尽(有时甚至令人筋疲力尽)的书,涵盖了 XSLT 故事中的每一个可能的基础。

https://www.amazon.com/XSLT-Programmers-Reference-Programmer/dp/0764543814

  • XSLT
    • 作者:Doug Tidwell
    • 页数:473 页
    • 出版商:O'Reilly Media;第一版 (2001 年 8 月 15 日)
    • ISBN: 0596000537
      • Doug Tidwell 是 IBM 的高级开发人员,也是 XML 技术领域的重要倡导者。他在 IBM 庞大的 XML 开发人员网站上撰写了多篇关于 XML 各个方面的文章和教程。这本书的全面性略逊于 Michael Kay 的著作,但它很好地涵盖了基础知识,并提供了一些引人入胜的示例。

https://www.amazon.com/Xslt-Doug-Tidwell/dp/0596000537

  • 学习 XML,第二版
    • 作者:Erik T. Ray
    • 页数:432 页
    • 出版商:O'Reilly Media;第二版 (2003 年 9 月 22 日)
    • ISBN: 0596004206
      • 正如标题所示,这是对 XML 的一般性概述。第 6 章专门讨论 XSLT。

https://www.amazon.com/gp/product/0596004206

规范

文章

教程/示例