XSLT文档中的模板以什么顺序执行,并且匹配源XML或缓冲输出?

这里总是让我对XSLT感到困惑:

  1. 模板以什么顺序执行,
  2. 当它们执行时,它们是否匹配(a)原始源XML,或(b)当前XSLT的输出到那个点?

例:

<person> <firstName>Deane</firstName> <lastName>Barker</lastName> </person> 

以下是XSLT的一个片段:

 <!-- Template #1 --> <xsl:template match="/"> <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/> </xsl:template> <!-- Template #2 --> <xsl:template match="/person/firstName"> First Name: <xsl:value-of select="firstName"/> </xsl:template> 

有两个问题:

  1. 我假设模板#1将首先执行。 我不知道为什么我这样做 – 是因为它出现在文件中的第一个?
  2. 将模板#2执行? 它匹配源XML中的一个节点,但是当我们到达这个模板(假设它运行第二个)时,“firstName”节点将不在输出树中。

那么,“以后”模板是否支持“早期”模板中发生的事情,还是在源文件上运行,而忽略了“先前”改变的内容? (所有这些单词都用引号引起来,因为当我真的不知道如何确定模板顺序时,我很难讨论基于时间的问题…)

在上面的例子中,我们有一个匹配根节点(“/”)的模板,当它完成执行时,已经从输出中删除了所有的节点。 在这种情况下,这是否会阻止所有其他模板执行,因为在第一个模板完成之后没有任何内容可以匹配?

到目前为止,我一直关注后来的模板没有执行,因为他们操作的节点没有出现在输出中,但是反过来呢? “较早”的模板能创build一个“稍后”的模板可以做些什么的节点吗?

在上面的XML中,考虑这个XSL:

 <!-- Template #1 --> <xsl:template match="/"> <fullName> <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/> </fullName> </xsl:template> <!-- Template #2 --> <xsl:template match="//fullName"> Full Name: <xsl:value-of select="."/> </xsl:template> 

模板#1创build一个名为“fullName”的新节点。 模板#2在同一个节点上匹配。 将模板#2执行,因为我们到模板#2的时候,输出中存在“fullName”节点?

我意识到我对XSLT的“禅”一无所知。 到目前为止,我的样式表由一个匹配根节点的模板组成,然后从那里完全程序化。 我厌倦了这样做。 我宁愿正确理解XSLT,因此我的问题。

我爱你的问题。 你很清楚你还不明白的东西。 你只需要一些东西来把事情联系在一起。 我的build议是,你阅读“XSLT如何工作” ,这是我写的一个章节,旨在解决你所问的问题。 我很想听听它是否把你的东西联系在一起。

不太正式的,我会刺探回答你的每一个问题。

  1. 模板以什么顺序执行,
  2. 当它们执行时,它们是否匹配(a)原始源XML,或(b)当前XSLT输出到那个点?

在XSLT处理的任何给定点上,从某种意义上说,有两种上下文,分别是(a)和(b):你在源树中的位置以及你在结果树中的位置 。 您在源代码树中的位置称为当前节点 。 它可以更改和跳转源代码树,因为您可以select使用XPath处理的任意节点集。 然而,从概念上说,你永远不会以同样的方式“跳过”结果树。 XSLT处理器以有序的方式构build它; 首先创build结果树的根节点; 然后添加子项,以文档顺序(深度优先)构build结果。 [您的post激励我再次拿起我的软件可视化XSLT实验…]

样式表中模板规则的顺序永远不重要。 通过查看样式表,您不能通过查看样式表来判断模板规则将被实例化的次序,实例化规则的次数,甚至根本不会。 ( match="/"是一个例外,你总是可以知道它会被触发。)

我假设模板#1将首先执行。 我不知道为什么我这样做 – 是因为它出现在文件中的第一个?

不。 即使将其放在文档中,也会先调用它。 模板规则的顺序永远没有问题(除非有一个以上的模板规则具有相同的优先级匹配相同的节点,即使这样,它是可选的实现者,你不应该依赖这种行为)的错误条件。 它首先被调用,因为每当运行XSLT处理器时总会发生的第一件事情是对<xsl:apply-templates select="/"/>的虚拟调用。 一个虚拟调用构造整个结果树。 它外面什么也没有发生 您可以通过定义模板规则来自定义或“configuration”该指令的行为。

将模板#2执行? 它匹配源XML中的一个节点,但是当我们到达这个模板(假设它运行第二个)时,“firstName”节点将不在输出树中。

除非在match="/"规则中的某个<xsl:apply-templates/>调用,否则模板#2(也不包括任何其他模板规则)将永远不会被触发。 如果没有,则不会触发match="/"以外的模板规则。 可以这么想:模板规则被触发,不能匹配input中的节点。 它必须匹配您select处理的节点(使用<xsl:apply-templates/> )。 相反,它会继续按照您select的次数来匹配节点。

由于在第一个模板完成之后没有任何东西匹配,[ match="/"模板]是否会抢先执行所有其他模板?

该规则通过无处不在,其中包括<xsl:apply-templates/> 。 仍然有很多节点可以在源码树中处理。 他们总是在那里,采摘成熟; 根据需要多次处理每一个。 但使用模板规则处理它们的唯一方法是调用<xsl:apply-templates/>

到目前为止,我一直关注后来的模板没有执行,因为他们操作的节点没有出现在输出中,但是反过来呢? “较早”的模板能创build一个“稍后”的模板可以做些什么的节点吗?

这并不是说“较早”的模板创build了一个新的节点来处理; 它是一个“较早的”模板依次使用相同的指令( <xsl:apply-templates )处理源树中的更多节点。 你可以把它看作recursion调用同一个“函数”,每次使用不同的参数(由上下文和select属性决定的要处理的节点)。

最后,你得到的是一个树形结构的对同一个“函数”( <xsl:apply-templates> )的recursion调用。 而这个树形结构与你的实际结果是同构的 。 并不是每个人都意识到这一点,或者这样想过; 那是因为我们没有任何有效的可视化工具。

模板#1创build一个名为“fullName”的新节点。 模板#2在同一个节点上匹配。 将模板#2执行,因为我们到模板#2的时候,输出中存在“fullName”节点?

不。 做一连串处理的唯一方法是明确地设置它。 创build一个variables,例如$tempTree ,它包含新的<fullName>元素,然后处理 ,就像这个<xsl:apply-templates select="$tempTree"> 。 要在XSLT 1.0中执行此操作,需要将variables引用包含扩展函数(例如exsl:node-set() ),但在XSLT 2.0中,它将按原样工作。

无论您是从原始源树还是在您构build的临时树中处理节点,都需要明确说明要处理的节点。

我们没有涉及到的是XSLT如何获得其隐含的行为。 您还必须了解内置的模板规则 。 我一直写样式表,甚至不包括根节点的显式规则( match="/" )。 相反,我依靠内置的根节点规则(将模板应用于子项),这与元素节点的内置规则相同。 因此,我可以忽略大部分的input,让XSLT处理器自动遍历它,只有当它遇到我感兴趣的节点时,我才会做一些特殊的事情。 或者我可以编写一个recursion复制一切的规则(称为身份转换),仅在必要时覆盖它,才能对input进行增量更改。 在阅读“XSLT如何工作”之后,您的下一个任务是查找“身份转换”。

我意识到我对XSLT的“禅”一无所知。 到目前为止,我的样式表由一个匹配根节点的模板组成,然后从那里完全程序化。 我厌倦了这样做。 我宁愿正确理解XSLT,因此我的问题。

我为你鼓掌。 现在是时候采取“红丸”:阅读“如何XSLT工作”

模板始终在源XML中匹配。 所以顺序并不重要,除非两个或多个模板匹配相同的节点。 在这种情况下,与直觉相反的是, 最后匹配模板的规则被触发。

在你的第一个例子模板#1运行,因为当你开始处理inputXML时,它从根开始,这是样式表中唯一与根元素相匹配的模板。 即使是样式表中的第二个,它仍然会运行第一个。

在这个例子中,模板2将不会运行,因为您已经使用模板1处理了根元素,并且在根之后没有更多元素需要处理。 如果您确实想要使用其他模板处理其他元素,则应将其更改为。

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

这样,您就可以为每个感兴趣的元素定义一个模板,并以更合理的方式处理xml,而不是通过程序来完成。

还要注意,这个例子不会输出任何东西,因为在当前上下文(根)没有firstName元素,只有一个人元素,所以它应该是:

 <xsl:template match="/"> <xsl:value-of select="person/firstName"/> <xsl:value-of select="person/lastName"/> </xsl:template> 

我觉得更容易认为你正在逐步通过XML,从根开始,寻找匹配该元素的模板,然后按照这些指令来生成输出。 XSLT将input文档转换为输出,以便在转换开始时输出文档是空的。 输出不被用作转换的一部分,它只是它的输出。

在你的第二个例子中,Template#2不会执行,因为模板是针对inputxml而不是输出运行的。

埃文的答案基本上是一个好的答案。

然而,似乎缺乏的一件事是能够“调用”大块的代码而不做任何匹配。 至less在某些人看来,这将会使得结构更好。

我做了一个小例子,试图说明我的意思。

 <xsl:template match="/" name="dotable"> <!-- Surely the common html part could be placed somewhere else --> <!-- the head and the opening body --> <html> <head><title>Salary table details</title></head> <body> <!-- Comments are better than nothing --> <!-- but that part should really have been somewhere else ... --> <!-- Now do what we really want here ... this really is making the table! --> <h1>Salary Table</h1> <table border = "3" width="80%"> <xsl:for-each select="//entry"> <tr> <td><xsl:value-of select="name" /></td> <td><xsl:value-of select="firstname" /></td> <td><xsl:value-of select="age" /></td> <td><xsl:value-of select="salary" /></td> </tr> </xsl:for-each> </table> <!-- Now close out the html --> </body> </html> <!-- this should also really be somewhere else --> <!-- This approach works, but leads to horribly monolithic code --> <!-- Further - it leads to templates including code which is strictly --> <!-- not relevant to them. I've not found a way round this yet --> </xsl:template> 

然而,摆弄了一下之后,首先使用提示,如果有两个匹配的模板,代码中的最后一个将被选中,然后重构我的代码(这里没有全部显示),我实现了这一点工作,并希望生成正确的代码,以及显示想要的数据 –

 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- <?xml version="1.0"?>--> <xsl:template name="dohtml"> <html> <xsl:call-template name="dohead" /> <xsl:call-template name="dobody" /> </html> </xsl:template> <xsl:template name="dohead"> <head> <title>Salary details</title> </head> </xsl:template> <xsl:template name="dobody"> <body> <xsl:call-template name="dotable" /> </body> </xsl:template> <xsl:template match="/entries" name="dotable"> <h1>Salary Table</h1> <table border = "3" width="80%"> <xsl:for-each select="//entry"> <tr> <td><xsl:value-of select="name" /></td> <td><xsl:value-of select="firstname" /></td> <td><xsl:value-of select="age" /></td> <td><xsl:value-of select="salary" /></td> </tr> </xsl:for-each> </table> </xsl:template> <xsl:template match="/" name="main"> <xsl:call-template name="dohtml" /> </xsl:template> 

[如果你看不到所有的东西,滚动上面的代码]

这种工作方式是主模板总是匹配 – 匹配/

这有代码块模板 – 被称为。

这意味着不可能在/上匹配另一个模板,但是可以明确地匹配一个命名节点,在这种情况下,它是xml中最高级别的节点 – 称为条目。

代码的一个小修改产生了上面给出的例子。