使用 XSLT 转换 XML 数据

可扩展样式表语言转换(XSLT)API 可以用于许多 Object。例如,使用足够智能的样式表,您可以从 XML 数据生成 PDF 或 PostScript 输出。但通常,XSLT 用于生成格式化的 HTML 输出,或创建数据的替代 XML 表示。

在本节中,将使用 XSLT 转换将 XMLImporting 数据转换为 HTML 输出。


注意- XSLT specification很大且很复杂,因此本教程只能刮擦表面。它将为您提供一些背景知识,以便您可以理解简单的 XSLT 处理任务,但不会详细研究如何编写 XSLT 转换,而是着重于如何使用 JAXP 的 XSLT 转换 API。有关 XSLT 的更全面基础,请查阅良好的参考手册,例如 Michael Kay 的 XSLT 2.0 和 XPath 2.0:程序员参考(Wrox,2008 年)。


定义简单的文档类型

首先定义一个非常简单的文档类型,该类型可用于撰写文章。我们的article文档将包含以下结构标签:

  • \<TITLE\>:文章标题

  • \<SECT\>:节,由标题和正文组成

  • \<PARA\>:一个段落

  • \<LIST\>:列表

  • \<ITEM\>:列表中的条目

  • \<NOTE\>:从主文本offset的一面

这种结构的一点不寻常之处在于,我们不会为节标题创建单独的元素标签。通常创建此类元素是为了将标题文本(及其包含的任何标签)与节的主体(即,标题下方的任何结构元素)区分开。

相反,我们将允许标题无缝合并到节的主体中。这种安排给样式表增加了一些复杂性,但是它将使我们有机会探索 XSLT 的模板选择机制。它也符合我们对文档结构的直观期望,其中标题文本后紧跟结构元素,这种结构可以简化面向轮廓的编辑。


注意- 这种结构不容易验证,因为 XML 的混合内容模型允许文本在节中的任何地方,而我们想限制文本和内联元素,使其仅出现在正文中的第一个结构元素之前的部分。基于 assert 的验证器可以做到这一点,但是大多数其他 Pattern 机制不能。因此,我们将无需为文档类型定义 DTD。


在此结构中,节可以嵌套。嵌套的深度将确定将哪种 HTML 格式用于节标题(例如h1h2)。在面向轮廓的编辑中,使用普通的SECT标记(而不是编号的节)也很有用,因为它使您可以随意移动节,而不必担心更改任何受影响节的编号。

对于列表,我们将使用 type 属性来指定列表条目是无序(项目符号),alpha(小写字母枚举),ALPHA(大写字母枚举)还是编号。

我们还将允许一些内联标签来更改文本的外观。

  • \<B\>:粗体

  • \<I\>:斜体

  • \<U\>:下划线

  • \<DEF\>:定义

  • \<LINK\>:链接到 URL


注意- 内嵌标签不会产生换行符,因此由内嵌标签引起的样式更改不会影响页面上文本的流动(尽管它将影响该文本的外观)。另一方面,结构标签划分了一个新的文本段,因此,除其他格式更改外,它至少还总是产生换行符。


\<DEF\>标记将用于文本中定义的术语。这些术语将以斜体显示,即它们通常在文档中的方式。但是在 XML 中使用特殊标记将使索引程序可以找到此类定义,并将其与标题中的关键字一起添加到索引中。例如,在前面的 注解 中,内联标签和结构标签的定义可能已用\<DEF\>标签标记,以供将来索引。

最后,LINK标签有两个作用。首先,它将使我们能够创建指向 URL 的链接,而不必将 URL 放入两次。因此我们可以用\<link\>http//\.\.\.\</link\>而不是\<a href="http//\.\.\."\>http//\.\.\.\</a\>进行编码。当然,我们还将希望允许使用\<link target="\.\.\."\>\.\.\.name\.\.\.\</link\>形式的表单。导致\<link\>标签的第二个原因。这将使我们有机会使用 XSLT 中的条件表达式。


注意- 尽管文章结构非常简单(仅由 11 个标签组成),但它提出了足够有趣的问题,使我们可以很好地了解 XSLT 的基本功能。但是,我们仍然会保留规范的大部分内容。在XSLT 还可以做什么?中,我们将指出我们跳过的主要功能。


创建测试文档

在这里,您将使用嵌套的\<SECT\>元素,一些\ 元素,\<NOTE\>元素,\<LINK\>\<LIST type="unordered"\>创建一个简单的测试文档。这个想法是创建一个包含所有内容的文档,以便我们可以探索更有趣的翻译机制。


注意- 本节讨论的代码位于article1\.xml,将XSLT examples解压缩到 install-dir /jaxp\-1_4_2\- release-date /samples目录后,该代码位于xslt/data目录中。


要制作测试文档,请创建一个名为article\.xml的文件,然后 Importing 以下 XML 数据。

<?xml version="1.0"?>
<ARTICLE>
   <TITLE>A Sample Article</TITLE>
   <SECT>The First Major Section
      <PARA>This section will introduce a subsection.</PARA>
      <SECT>The Subsection Heading
         <PARA>This is the text of the subsection.
         </PARA>
      </SECT>
   </SECT>
</ARTICLE>

请注意,在 XML 文件中,该小节完全包含在 main 小节中。 (另一方面,在 HTML 中,标题不包含节的主体)。结果是,这样的轮廓结构很难以纯文本格式进行编辑,但是使用面向轮廓的编辑器进行编辑则容易得多。

某天,给定一个能够理解内联标签(例如\<B\>\<I\>)的面向树的 XML 编辑器,应该可以以大纲形式编辑此类文章,而无需复杂的样式表。 (这样的编辑器将使作者能够专注于文章的结构,而将版面保留到该过程的后面)。在这样的编辑器中,文章片段将如下所示:

<ARTICLE> 
 <TITLE>A Sample Article 
  <SECT>The First Major Section 
   <PARA>This section will 
            introduce a subsection.
    <SECT>The Subheading 
     <PARA>This is the text of the subsection. 
         Note that ...

注意- 目前,树状结构的编辑器存在,但是它们以处理结构标签的相同方式来处理诸如\<B\>\<I\>之类的内联标签,这会使“轮廓”有点难以阅读。


编写 XSLT 转换

现在是时候开始编写 XSLT 转换了,该转换将转换 XML 文章并将其呈现为 HTML。


注意- 本节讨论的代码位于article1a\.xsl,将XSLT examples解压缩到 install-dir /jaxp\-1_4_2\- release-date /samples目录后,该代码位于xslt/data目录中。


首先创建一个普通的 XML 文档:

<?xml version="1.0" encoding="ISO-8859-1"?>

然后添加以下突出显示的行以创建 XSL 样式表:

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

</xsl:stylesheet>

现在将其设置为产生 HTML 兼容的输出。

<xsl:stylesheet 
[...]

   >
<xsl:output method="html"/>

[...]

</xsl:stylesheet>

我们将在本节后面的部分中详细介绍该条 Object 详细原因。现在,请注意,如果您要输出格式正确的 XML 以外的任何内容,则将需要一个\<xsl:output\>标记(如所示),指定texthtml。 (默认值为xml)。


注意- 当您指定 XML 输出时,您可以添加 indent 属性以生成缩进的 XML 输出。规范如下所示:\<xsl:output method="xml" indent="yes"/\>


处理基本结构元素

您将通过处理创建目录的元素开始填充样式表:根元素,标题元素和标题。您还将处理测试文档中定义的PARA元素。


注意- 如果您在初读时跳过了讨论 XPath 寻址机制的部分XPath 的工作方式,那么现在是回顾该部分的好时机。


首先添加处理根元素的主要指令:

<xsl:template match="/">
      <html><body>
         <xsl:apply-templates/>
      </body></html>
   </xsl:template>

</xsl:stylesheet>

新的 XSL 命令以粗体显示。 (请注意,它们是在xsl名称空间中定义的)。指令\<xsl:apply\-templates\>处理当前节点的子代。在这种情况下,当前节点是根节点。

尽管它很简单,但是该示例说明了许多重要的想法,因此值得彻底理解。第一个概念是样式表包含许多由\<xsl:template\>标签定义的模板。每个模板都包含一个 match 属性,该属性使用XPath 的工作方式中描述的 XPath 寻址机制来选择要应用于模板的元素。

在模板内,仅复制不以xsl: namespace前缀开头的标签。跟随它们的换行符和空格也会被复制,这有助于使结果输出可读。


注意- 当不存在换行符时,通常会忽略空格。在这种情况下,要在输出中包含空格,或要包含其他文本,可以使用\<xsl:text\>标记。基本上,XSLT 样式表希望处理标签。因此,它看到的所有内容都必须是\<xsl:\.\.\>标记,其他标记或空白。


在这种情况下,非 XSL 标签是 HTML 标签。因此,当根标记匹配时,XSLT 输出 HTML 起始标记,处理适用于根子代的所有模板,然后输出 HTML 结束标记。

处理\ 元素

接下来,添加一个模板来处理文章标题:

<xsl:template match="/ARTICLE/TITLE">
 <h1 align="center"> 
    <xsl:apply-templates/> </h1>
 </xsl:template>

</xsl:stylesheet>

在这种情况下,您可以指定 TITLE 元素的完整路径并输出一些 HTML,以使标题文本变成一个大的居中标题。在这种情况下,apply\-templates标签可确保标题包含任何内联标签(例如,斜体,链接或下划线),也将对其进行处理。

更重要的是,apply\-templates指令使标题文本得以处理。像 DOM 数据模型一样,XSLT 数据模型基于元素节点中包含的文本节点的概念(而文本节点又可以包含在其他元素节点中,依此类推)。该层次结构构成了源代码树。还有一个结果树,其中包含输出。

XSLT 通过将源树转换为结果树来工作。为了使 XSLT 操作的结果可视化,有助于理解这些树的结构及其内容。 (有关此主题的更多信息,请参见XSLT/XPath 数据模型)。

Process Headings

要 continue 处理基本结构元素,请添加一个模板来处理顶级标题:

<xsl:template match=
    "/ARTICLE/SECT">
 <h2> <xsl:apply-templates
 select="text()|B|I|U|DEF|LINK"/> 
</h2>
 <xsl:apply-templates select=
    "SECT|PARA|LIST|NOTE"/>
 </xsl:template>

</xsl:stylesheet>

在这里,您指定最顶部的SECT元素的路径。但是这一次,您可以使用select属性分两个阶段应用模板。在第一阶段,使用 XPath text\(\)函数选择文本节点以及内联标签,例如粗体和斜体。 (垂直管道(\|)用于匹配多个项目:文本或粗体标签或斜体标签等)。在第二阶段中,为节,段落,列表和 注解 选择文件中包含的其他结构元素。

使用 select 属性,可以将 text 和 inline 元素放在\<h2\>\.\.\.\</h2\>标记之间,同时确保随后处理该节中的所有结构标记。换句话说,您要确保 XML 文档中标题的嵌套不会反映在 HTML 格式中,这对于 HTML 输出很重要。

通常,使用 select 子句可使您将所有模板应用于当前上下文中可用信息的子集。另一个示例,此模板选择当前节点的所有属性:

<xsl:apply-templates select="@*"/></attributes>

接下来,添加几乎相同的模板来处理嵌套在更深一层的子标题:

<xsl:template match=
    "/ARTICLE/SECT/SECT">
 <h3> <xsl:apply-templates
 select="text()|B|I|U|DEF|LINK"/> 
</h3>
 <xsl:apply-templates select=
    "SECT|PARA|LIST|NOTE"/>
 </xsl:template>

</xsl:stylesheet>

生成运行时消息

您也可以添加用于更深标题的模板,但是在某些时候,您必须停止,这仅仅是因为 HTML 仅下降到五个级别。对于此示例,您将在两个级别的节标题处停止。但是,如果 XMLImporting 恰好包含第三级,则将需要向用户传递错误消息。本节说明如何执行此操作。


注意- 我们可以 continue 处理SECT个元素,方法是使用表达式/SECT/SECT//SECT选择它们。 //根据 XPath 寻址机制的定义,在任何深度选择任何SECT元素。但是相反,我们将抓住机会来处理消息传递。


当遇到嵌套太深的部分时,添加以下模板以生成错误:

<xsl:template match=
    "/ARTICLE/SECT/SECT/SECT">
 <xsl:message terminate="yes">
 Error: Sections can only be nested 2 deep.
 </xsl:message>
 </xsl:template>

</xsl:stylesheet>

terminate="yes"子句导致生成消息后转换过程停止。没有它,处理仍然可以 continue 进行,该部分中的所有内容都将被忽略。

作为附加的练习,您可以扩展样式表以处理最多嵌套到四个部分的部分,生成\<h2\>\.\.\.\<h5\>标签。在嵌套五个级别的任何节上生成错误。

最后,通过添加模板来处理PARA标签来完成样式表:

<xsl:template match="PARA">
 <p><xsl:apply-templates/></p>
 </xsl:template>
</xsl:stylesheet>

编写基本程序

现在,您将修改使用 XSLT 来回显不变的 XML 文件的程序,并对其进行更改以使其使用样式表。


注意- 本节讨论的代码位于Stylizer\.java,将XSLT examples解压缩到 install-dir /jaxp\-1_4_2\- release-date /samples目录后,该代码位于xslt目录中。结果为stylizer1a\.html,位于xslt/data中。


Stylizer示例是从TransformationApp02改编而成的,该TransformationApp02解析 XML 文件并将其写入System\.out。这两个程序之间的主要区别如下所述。

首先,Stylizer在创建Transformer对象时使用样式表。

// ...
import javax.xml.transform.dom.DOMSource; 
import javax.xml.transform.stream.StreamSource; 
import javax.xml.transform.stream.StreamResult; 
// ... 

public class Stylizer {
    // ...
    public static void main (String argv[]) {
        // ...
        try {
            File stylesheet = new File(argv[0]);
            File datafile = new File(argv[1]);

            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.parse(datafile);
            // ...
            StreamSource stylesource = new StreamSource(stylesheet); 
            Transformer transformer = Factory.newTransformer(stylesource);
        }
    }
}

此代码使用该文件创建一个StreamSource对象,然后将源对象传递给工厂类以获取转换器。


注意- 您可以通过消除DOMSource类来简化代码。不要为 XML 文件创建DOMSource对象,而是为其和样式表创建StreamSource对象。


运行样式器示例

  • 导航到samples目录.
% cd install-dir/jaxp-1_4_2-release-date/samples.
cd xslt
  • 编译Stylizer示例.

键入以下命令:

% javac Stylizer.java
  • 使用样式表article1a\.xslarticle1\.xml上运行Stylizer示例.
% java Stylizer data/article1a.xsl  data/article1.xml

您将看到以下输出:

<html>
<body>

<h1 align="center">A Sample Article</h1>
<h2>The First Major Section

</h2>
<p>This section will introduce a subsection.</p>
<h3>The Subsection Heading

</h3>
<p>This is the text of the subsection.
</p>

</body>
</html>

此时,输出中有很多多余的空格。在下一节中,您将看到如何消除其中的大部分。

修剪空白

回想一下,当您查看 DOM 的结构时,有许多文本节点只包含可忽略的空白。输出中大多数多余的空白来自这些节点。幸运的是,XSL 为您提供了一种消除它们的方法。 (有关节点结构的更多信息,请参见XSLT/XPath 数据模型)。


注意- 本节讨论的样式表位于article1b\.xsl,将XSLT examples解压缩到 install-dir /jaxp\-1_4_2\- release-date /samples目录后,该样式表位于xslt/data目录中。结果为stylizer1b\.html,位于xslt/data中。


要删除一些多余的空格,请在样式表中添加以下突出显示的行。

<xsl:stylesheet ...
   >
<xsl:output method="html"/> 
<xsl:strip-space elements="SECT"/>

[...]

该指令告诉 XSL 删除SECT元素下除空格外仅包含任何文本节点。包含非空格文本的节点将不受影响,其他类型的节点也将不受影响。

使用修饰的空白运行样式器示例

  • 导航到samples目录.
% cd install-dir/jaxp-1_4_2-release-date/samples.
cd xslt
  • 编译Stylizer示例.

键入以下命令:

% javac Stylizer.java
  • 使用样式表article1b\.xslarticle1\.xml上运行Stylizer示例.
% java Stylizer 
  data/article1b.xsl  
  data/article1.xml

您将看到以下输出:

<html>
<body>

<h1 align="center">A Sample Article</h1>

<h2>The First Major Section
   </h2>
<p>This section will introduce a subsection.</p>
<h3>The Subsection Heading
      </h3>
<p>This is the text of the subsection.
      </p>

</body>
</html>

那是一个很大的进步。标题后面仍然有换行符和空格,但是它们来自于 XML 的编写方式:

<SECT>The First Major Section
____<PARA>This section will introduce a subsection.</PARA>
^^^^

在这里,您可以看到在 PARA 条目开始之前,节标题以换行符和缩进空间结尾。不必担心,因为将处理 HTML 的浏览器会压缩并例行忽略多余的空间。但是,还有另外一种格式化工具可供我们使用。

删除最后一个空格


注意- 本节讨论的样式表位于article1c\.xsl,将XSLT examples解压缩到 install-dir /jaxp\-1_4_2\- release-date /samples目录后,该样式表位于xslt/data目录中。结果为stylizer1c\.html,位于xslt/data中。


通过在样式表中添加以下内容来处理最后一点空白:

<xsl:template match="text()">
 <xsl:value-of select="normalize-space()"/>
 </xsl:template>

</xsl:stylesheet>

使用此样式表运行Stylizer将删除所有剩余的空格。

修剪所有空白的样式器示例

  • 导航到samples目录.
% cd install-dir/jaxp-1_4_2-release-date/samples.
cd xslt
  • 编译Stylizer示例.

键入以下命令:

% javac Stylizer.java
  • 使用样式表article1c\.xslarticle1\.xml上运行Stylizer示例.
% java Stylizer 
  data/article1c.xsl  
  data/article1.xml

现在的输出如下所示:

<html>
<body>
<h1 align="center">A Sample Article
</h1>
<h2>The First Major Section</h2>
<p>This section will introduce a subsection.
</p>
<h3>The Subsection Heading</h3>
<p>This is the text of the subsection.
</p>
</body>
</html>

那好多了。当然,如果缩进会更好,但是事实证明这比预期要难。以下是一些可能的攻击途径以及困难:

  • Indent option

    • 不幸的是,可以应用于 XML 输出的indent="yes"选项不适用于 HTML 输出。即使该选项可用,也无济于事,因为 HTML 元素很少嵌套!尽管 HTML 源代码经常缩进以显示隐式结构,但是 HTML 标记本身并未以创建真实结构的方式嵌套。
  • Indent variables

    • \<xsl:text\>函数使您可以添加所需的任何文本,包括空格。因此可以想象它可用于输出缩进空间。问题是改变缩进空间的数量。 XSLT 变量似乎是个好主意,但在这里不起作用。原因是,当您为模板中的变量分配值时,该值仅在该模板内才知道(静态,在编译时)。即使变量是全局定义的,分配的值也不会以其他模板在运行时动态知道的方式存储。当\<apply\-templates/\>调用其他模板时,这些模板不知道在其他地方进行的任何变量设置。
  • Parameterized templates

    • 使用参数化模板是修改模板行为的另一种方法。但是确定作为参数传递的缩进空间量仍然是问题的症结所在。

因此,目前似乎没有任何好的方法来控制 HTML 格式输出的缩进。如果您需要将 HTML 显示或编辑为纯文本,这将很不方便。但是,如果您在 XML 表单上进行编辑(使用仅在浏览器中显示的 HTML 版本)则没有问题。 (例如,当您查看stylizer1c\.html时,会看到预期的结果)。

处理剩余的结构元素

在本节中,您将处理LISTNOTE元素,这将为文章添加更多结构。


注意- 本节中描述的 samples 文档是article2\.xml,用于操作它的样式表是article2\.xsl。结果是stylizer2\.html。将XSLT examples解压缩到 install-dir /jaxp\-1_4_2\- release-date /samples目录后,可以在xslt/data目录中找到这些文件。


首先将一些测试数据添加到 samples 文档中:

<?xml version="1.0"?>
<ARTICLE>
<TITLE>A Sample Article</TITLE>
 <SECT>The First Major Section
    ...
  </SECT>
  <SECT>The Second Major Section
  <PARA>This section adds a LIST and a NOTE.
    <PARA>Here is the LIST:
      <LIST type="ordered">
        <ITEM>Pears</ITEM>
        <ITEM>Grapes</ITEM>
      </LIST>
  </PARA>
  <PARA>And here is the NOTE:
  <NOTE>Don't forget to go to the 
           hardware store on your way
           to the grocery!
  </NOTE>
  </PARA>
 </SECT> 
</ARTICLE>

注意- 尽管 XML 文件中的listnote包含在它们各自的段落中,但实际上是否包含它们并没有区别。两种方法生成的 HTML 相同。但是,将它们包含起来将使它们在面向概述的编辑器中更易于处理。


修改\ 处理

接下来,修改PARA模板,以解决我们现在允许将某些结构元素嵌入段落的事实:

<xsl:template match="PARA">
<p> <xsl:apply-templates select=
    "text()|B|I|U|DEF|LINK"/>
 </p>
 <xsl:apply-templates select=
    "PARA|LIST|NOTE"/>
</xsl:template>

此修改使用与节标题相同的技术。唯一的区别是,段落中不应包含SECT个元素。 (但是,一个段落很容易存在于另一个段落中-例如,作为引用的材料)。

处理\ 和\ 元素

现在,您可以添加模板来处理LIST个元素:

<xsl:template match="LIST">
 <xsl:if test="@type='ordered'"> 
  <ol>
   <xsl:apply-templates/>
    </ol>
    </xsl:if>
    <xsl:if test="@type='unordered'">
     <ul>
      <xsl:apply-templates/>
     </ul>
 </xsl:if>
</xsl:template>

</xsl:stylesheet>

\<xsl:if\>标记使用test=""属性指定布尔条件。在这种情况下,将测试 type 属性的值,并且生成的列表将根据该值是有序还是无序而变化。

请注意此示例中的两个重要事项:

  • 没有 else 子句,也没有 return 或 exit 语句,因此需要两个\<xsl:if\>标记来覆盖这两个选项。 (或者可以使用\<xsl:choose\>标记,该标记提供了案例 语句 功能)。

  • 在属性值周围需要单引号。否则,XSLT 处理器将try将排序的单词解释为 XPath 函数而不是字符串。

现在,通过处理 ITEM 元素完成 LIST 处理:

<xsl:template match="ITEM">
 <li><xsl:apply-templates/>
 </li>
 </xsl:template>

</xsl:stylesheet>

样式表中的 Order 模板

到目前为止,您应该已经有了模板相互独立的想法,因此它们在文件中的位置通常无关紧要。因此,从现在开始,我们将仅显示您需要添加的模板。 (为了进行比较,它们总是添加在示例样式表的末尾)。

当两个模板可以应用于同一节点时,Sequences 确实有所不同。在这种情况下,最后定义的是找到并处理的那个。例如,要更改缩进列表的 Sequences 以使用小写字母,可以指定如下所示的模板 Pattern://LIST//LIST。在该模板中,您将使用 HTML 选项来生成字母枚举,而不是数字枚举。

但是,也可以通过 Pattern//LIST标识此类元素。为了确保完成正确的处理,指定//LIST的模板必须出现在指定//LIST//LIST的模板之前。

处理\ 元素

剩下的最后一个结构元素是NOTE元素。添加以下模板来处理。

<xsl:template match="NOTE">
 <blockquote><b>Note:</b>
<xsl:apply-templates/> </p></blockquote> </xsl:template> </xsl:stylesheet>

这段代码提出了一个有趣的问题,该问题是由于包含\<br/\>标签引起的。为了使文件格式正确,XML 必须在样式表中将标记指定为\<br/\>,但是许多浏览器无法识别该标记。并且尽管大多数浏览器都能识别\<br\>\</br\>序列,但它们都将其视为段落分隔符而不是单个换行符。

换句话说,转换必须生成\<br\>标签,但是样式表必须指定\<br/\>。这使我们想到了我们在样式表的开头添加的特殊输出标签的主要原因:

<xsl:stylesheet ... >
   <xsl:output method="html"/>
   [...]
</xsl:stylesheet>

该输出规范在输出时将空标记(例如\<br/\>)转换为其 HTML 格式\<br\>。该转换很重要,因为大多数浏览器无法识别空标记。以下是受影响的标签的列表:

area      frame   isindex
base      hr      link
basefont  img     meta
br        input   param
col

总而言之,默认情况下,XSLT 在输出中生成格式正确的 XML。而且,由于 XSL 样式表一开始是格式良好的 XML,因此您不能轻易在其中间放置诸如\<br\>之类的标签。 \<xsl:output method="html"/\>标记解决了该问题,因此您可以在样式表中编码\<br/\>,但在输出中获得\<br\>

指定\<xsl:output method="html"/\>的另一个主要原因是,与规范\<xsl:output method="text"/\>一样,生成的文本不会转义。例如,如果样式表包含\<实体引用,它将在生成的文本中显示为\<字符。另一方面,当生成 XML 时,样式表中的\<实体引用将保持不变,因此在生成的文本中将以\<的形式出现。


注意- 如果您实际希望将\<作为 HTML 输出的一部分生成,则需要将其编码为&lt;。该序列在输出时变为\<,因为只有&被转换为&字符。


使用定义的 LIST 和 NOTE 元素运行样式器 samples

  • 导航到samples目录.
% cd install-dir/jaxp-1_4_2-release-date/samples.
cd xslt
  • 编译Stylizer示例.

键入以下命令:

% javac Stylizer.java
  • 使用样式表article2\.xslarticle2\.xml上运行Stylizer示例.
% java Stylizer data/article2.xsl  data/article2.xml

这是立即运行程序时为第二部分生成的 HTML:

...
<h2>The Second Major Section
</h2>
<p>This section adds a LIST and a NOTE.
</p>
<p>Here is the LIST:
</p>
<ol>
<li>Pears</li>
<li>Grapes</li>
</ol>
<p>And here is the NOTE:
</p>
<blockquote>
<b>Note:</b>
<br>Do not forget to go to the hardware store on your way to the grocery!
</blockquote>

处理内联(内容)元素

ARTICLE类型中剩余的唯一标记是内联标记,它们不会在输出中创建换行符,而是集成到它们所属的文本流中。

内联元素与结构元素的不同之处在于,内联元素是标签内容的一部分。如果您将元素视为文档树中的一个节点,则每个节点都具有内容和结构。内容由包含的文本和内联标签组成。结构由标签下的其他元素(结构元素)组成。


注意- 本节中描述的 samples 文档是article3\.xml,用于操作它的样式表是article3\.xsl。结果是stylizer3\.html


首先将更多测试数据添加到示例文档中:

<?xml version="1.0"?>
<ARTICLE>
 <TITLE>A Sample Article</TITLE>
  <SECT>The First Major Section
      [...]
  </SECT>
  <SECT>The Second Major Section
      [...]
  </SECT> 
<SECT>The <i>Third</i> 
    Major Section
 <PARA>In addition to the inline tag
    in the heading, 
 this section defines the term  
    <DEF>inline</DEF>,
 which literally means "no line break". 
 It also adds a simple link to the main page 
    for the Java platform 
(<LINK>http://java.sun.com</LINK>),
 as well as a link to the 
 <LINK target="http://java.sun.com/xml">
   XML </LINK> 
 page.
 </PARA>
 </SECT> 
</ARTICLE>

现在,处理段落中的内联\<DEF\>元素,将其重命名为 HTML 斜体标签:

<xsl:template match="DEF">
 <i> <xsl:apply-templates/> </i> 
</xsl:template>

接下来,注解 掉文本节点规范化。它已经达到了 Object,现在您需要保留重要的空间:

<!--  
<xsl:template match="text()">
  <xsl:value-of select="normalize-space()"/>
   </xsl:template>
-->

此修改使我们避免在诸如\<I\>\<DEF\>之类的标签之前丢失空格。 (请try不进行此修改的程序以查看结果)。

现在处理基本的内联 HTML 元素,例如\<B\>\<I\>\<U\>,以粗体,斜体和下划线显示。

<xsl:template match="B|I|U">
 <xsl:element name="{name()}">
 <xsl:apply-templates/>
 </xsl:element> 
</xsl:template>

\<xsl:element\>标签可让您计算要生成的元素。在这里,您可以使用当前元素的名称生成适当的内联标签。特别要注意name="\.\."表达式中花括号(\{\})的使用。这些花括号将引号内的文本作为 XPath 表达式处理,而不是解释为 Literals 字符串。在这里,它们使 XPath name\(\)函数返回当前节点的名称。

在可能出现属性值模板的任何地方都可以识别出大括号。 (属性值模板在 XSLT 规范的 7.6.2 节中定义,并且它们在模板定义中显示在多个位置)。在这样的表达式中,花括号也可以用于引用属性值\{@foo\}或元素\{foo\}的内容。


注意- 您也可以使用\<xsl:attribute\>生成属性。有关更多信息,请参见 XSLT 规范的 7.1.3 节。


剩下的最后一个元素是LINK标签。处理该标签的最简单方法是设置一个可以使用参数驱动的命名模板:

<xsl:template name="htmLink">
 <xsl:param name="dest" 
    select="UNDEFINED"/> 
 <xsl:element name="a">
 <xsl:attribute name="href">
 <xsl:value-of select=""/>
 </xsl:attribute>
 <xsl:apply-templates/> 
 </xsl:element> 
</xsl:template>

此模板的主要区别在于,您无需指定 match 子句,而是使用name=""子句为模板命名。因此,仅当您调用该模板时,才会执行该模板。

在模板中,您还可以使用\<xsl:param\>标签指定一个名为dest的参数。为了进行一些错误检查,您可以使用 select 子句为该参数赋予默认值UNDEFINED。要在\<xsl:value\-of\>标记中引用变量,请指定``。


注意- 请记住,引号中的条目将被解释为表达式,除非将其进一步用单引号引起来。这就是为什么在"@type='ordered'"之前需要单引号以确保将 ordered 解释为字符串 的原因。


\<xsl:element\>标签生成一个元素。以前,您可以通过编码\<html\>来简单地指定所需的元素。但是在这里,您正在\<xsl:element\>标记的主体中动态生成 HTML 锚(\<a\>)的内容。您正在使用\<xsl:attribute\>标签动态生成锚点的href属性。

模板的最后一个重要部分是\<apply\-templates\>标记,该标记将文本节点中的文本插入LINK元素下方。没有它,生成的 HTML 链接中将没有文本。

接下来,为LINK标签添加模板,并在其中调用命名模板:

<xsl:template match="LINK">
 <xsl:if test="@target">
 <!--Target attribute specified.-->
 <xsl:call-template 
    name="htmLink">
 <xsl:with-param name="dest" 
    select="@target"/> 
 </xsl:call-template>
 </xsl:if>
</xsl:template>
<xsl:template name="htmLink">

[...]

如果 LINK 标记中存在目标属性,则test="@target"子句返回 true。因此,当链接的文本和为其定义的目标不同时,此\<xsl\-if\>标记将生成 HTML 链接。

\<xsl:call\-template\>标签调用命名的模板,而\<xsl:with\-param\>使用 name 子句指定一个参数,并使用 select 子句指定其值。

作为样式表构建过程的最后一步,请将\<xsl\-if\>标签添加到没有目标属性的LINK标签中。

<xsl:template match="LINK">
   <xsl:if test="@target">
      [...]
   </xsl:if>

   <xsl:if test="not(@target)">
 <xsl:call-template name="htmLink">
 <xsl:with-param name="dest">
 <xsl:apply-templates/>
 </xsl:with-param>
 </xsl:call-template>
 </xsl:if>
</xsl:template>

not\(\.\.\.\)子句反转先前的测试(请记住,没有 else 子句)。因此,当未指定 target 属性时,将解释模板的这一部分。这次,参数值不是来自 select 子句,而是来自\<xsl:with\-param\>元素的内容。


注意- 为了明确起见:参数和变量(在XSLT 还可以做什么?中稍待讨论的参数和变量,其值可以由 select 子句指定,该子句允许您使用 XPath 表达式,也可以由元素的内容指定) ,使您可以使用 XSLT 标签。


在这种情况下,参数的内容由\<xsl:apply\-templates/\>标记生成,该标记将文本节点的内容插入LINK元素下方。

使用定义的内联元素运行样式器 samples

  • 导航到samples目录.
% cd install-dir/jaxp-1_4_2-release-date/samples.
cd xslt
  • 编译Stylizer示例.

键入以下命令:

% javac Stylizer.java
  • 使用样式表article3\.xslarticle3\.xml上运行Stylizer示例.
% java Stylizer data/article3.xsl  data/article3.xml

现在运行程序时,结果应如下所示:

[...]
<h2>The <i>Third</i> Major Section</h2>
<p>In addition to the inline tag in the heading, this section  defines the term <i>inline</i>, which literally means "no line break". It also adds a simple link to the main page for the Java platform (<a href="http://java.sun.com">http://java.sun.com</a>),  as well as a link to the <a href="http://java.sun.com/xml">XML</a> page.</p>

干得好!现在,您已经将一个相当复杂的 XML 文件转换为 HTML。 (就像乍看起来一样简单,它无疑为探索提供了很多机会)。

打印 HTML

现在,您已将 XML 文件转换为 HTML。有一天,有人会产生一个支持 HTML 的打印引擎,您可以通过 Java Printing Service API 查找和使用它。届时,您将能够通过生成 HTML 来打印任意 XML 文件。您要做的就是设置样式表并使用浏览器。

XSLT 还可以做什么?

与本节一样冗 Long,它仅涉及 XSLT 功能的表面。 XSLT 规范中还有许多其他可能性在 await 您。以下是一些要寻找的东西:

  • import(第 2.6.2 节)和include(第 2.6.1 节)

    • rt(第 2.6.2 节)和 include(第 2.6.1 节)使用这些语句来模块化和组合 XSLT 样式表。 include 语句仅插入包含文件中的所有定义。 import 语句使您可以使用自己的样式表中的定义覆盖导入文件中的定义。
  • for\-each循环(第 8 节)

    • 循环查看一组项目,然后依次处理每个项目。
  • choose(条件 语句)用于条件处理(第 9.2 节)

    • 根据 Importing 值分支到多个处理路径之一。
  • 生成数字(第 7.7 节)

    • 动态生成编号的节,编号的元素和数字 Literals。 XSLT 提供了三种编号 Pattern:
  • 单个:在单个标题下编号项目,例如 HTML 中的有序列表

  • 多个:产生多级编号,例如“ A.1.3”

  • 任意:连续编号项目(无论出现在何处),如类中的脚注一样。

  • 格式化数字(第 12.3 节)

    • 控制枚举格式,以便获得数字(format="1"),大写字母(format="A"),小写字母(format="a")或复合数字(例如“ A.1”)以及适合特定国际语言环境的数字和货币金额。
  • 排序输出(第 10 节)

    • 以所需的排序 Sequences 产生输出。
  • 基于 Pattern 的模板(第 5.7 节)

    • 多次处理元素,每次以不同的“Pattern”进行。您将 Pattern 属性添加到模板,然后指定\<apply\-templates mode="\.\.\."\>以仅应用具有匹配 Pattern 的模板。与\<apply\-templates select="\.\.\."\>属性结合使用,将基于 Pattern 的处理应用于 Importing 数据的子集。
  • 变量(第 11 节)

    • 变量类似于方法参数,因为它们使您可以控制模板的行为。但是它们并不像您想象的那样有价值。变量的值仅在定义它的当前模板或\<xsl:if\>标记(例如)的范围内是已知的。您不能将值从一个模板传递到另一个模板,甚至不能从模板的封闭部分传递到同一模板的另一部分。

即使对于“全局”变量,这些语句也适用。您可以在模板中更改其值,但是更改仅适用于该模板。当评估用于定义全局变量的表达式时,该评估在结构的根节点的上下文中进行。换句话说,全局变量本质上是运行时常量。这些常量对于更改模板的行为很有用,尤其是在与 include 和 import 语句结合使用时。但是变量不是通用的数据 管理 机制。

变量问题

试图创建单个模板并为链接的目标设置变量是很诱人的,而不是麻烦设置参数化模板并以两种不同方式调用它。想法是将变量设置为默认值(例如LINK标签的文本),然后,如果目标属性存在,则将目标变量设置为目标属性的值。

如果可行,那将是一个好主意。但同样,问题在于变量仅在定义它们的范围内是已知的。因此,当您对\<xsl:if\>标签进行编码以更改变量的值时,该值仅在\<xsl:if\>标签的上下文中才是已知的。一旦遇到\</xsl:if\>,对变量设置的任何更改都将丢失。

一个类似的诱人想法是可以用变量(``)代替text\(\)\|B\|I\|U\|DEF\|LINK规范。但是因为变量的值由定义的位置决定,所以全局内联变量的值由文本节点,\<B\>节点等组成,它们恰好在根级别存在。换句话说,这种变量的值在这种情况下为空。