Skip to main content
 首页 » 编程设计

xslt之XSLT 文档中的模板以什么顺序执行,它们是否与源 XML 或缓冲输出匹配

2024年02月27日10cmt

关于 XSLT,有一点一直让我感到困惑:

  • 模板以什么顺序执行,
  • 当它们执行时,它们是否与 (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 将首先执行。我不知道为什么我会这样认为——仅仅是因为它首先出现在文档中吗?
  • 模板#2 会执行吗?它匹配源 XML 中的一个节点,但是当我们到达这个模板时(假设它运行第二个),“firstName”节点将不在输出树中。

  • 那么,“后来的”模板是否受制于“较早的”模板中发生的事情,或者它们是否对源文档进行了操作,而忽略了它们“之前”已转换的内容? (所有这些词都用引号引起来,因为当我真的不知道如何首先确定模板顺序时,我发现很难讨论基于时间的问题......)

    在上面的例子中,我们有一个匹配根节点(“/”)的模板——当它完成执行时——基本上从输出中删除了所有节点。在这种情况下,由于在第一个模板完成后没有任何匹配项,这是否会抢占所有其他模板的执行?

    到目前为止,我一直担心后来的模板没有执行,因为它们操作的节点没有出现在输出中,但是相反的呢? “较早的”模板可以创建一个“较晚的”模板可以使用的节点吗?

    在与上面相同的 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 创建一个名为“fullName”的新节点。模板 #2 在同一节点上匹配。模板#2 是否会因为“fullName”节点在我们处理模板#2 时存在于输出中而执行?

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

    请您参考如下方法:

    我喜欢你的问题。你对你还不明白的东西非常清楚。你只需要一些东西来把事情联系在一起。我的建议是你阅读"How XSLT Works" ,我写的一章正是为了解决您提出的问题。我很想听听它是否为你把事情联系在一起。

    不太正式,我将尝试回答您的每个问题。

    1. In what order do the templates execute, and
    2. When they execute, do they match on (a) the original source XML, or (b) the current output of the XSLT to that point?


    在 XSLT 处理的任何给定点,从某种意义上说,有两个上下文,您将它们标识为 (a) 和 (b):您在源树中的位置,以及您在结果树中的位置。您在源树中的位置称为当前节点。当您选择使用 XPath 处理的任意节点集时,它可以在源树周围更改和跳转。但是,从概念上讲,您永远不会以相同的方式“跳过”结果树。 XSLT 处理器以有序的方式构造它;首先它创建结果树的根节点;然后它添加子项,按文档顺序(深度优先)构建结果。 [你的帖子激励我再次拿起我的软件可视化进行 XSLT 实验......]

    样式表中模板规则的顺序从不重要。仅通过查看样式表,您无法判断模板规则的实例化顺序、规则实例化的次数,甚至是否会实例化。 ( match="/" 是一个异常(exception);你总是可以知道它会被触发。)

    I am assuming that Template #1 will execute first. I don't know why I assume this -- is it just because it appears first in the document?



    不。即使你把它放在文档的最后,它也会首先被调用。模板规则顺序从不重要(除非在错误情况下,当您有多个具有相同优先级的模板规则匹配同一个节点时;即使如此,它对实现者来说也是可选的,您不应该依赖这种行为)。它首先被调用,因为 总是 每当您运行 XSLT 处理器时都会发生这种情况是对 <xsl:apply-templates select="/"/> 的虚拟调用.一个虚拟调用构建了整个结果树。在它之外什么也不会发生。您可以通过定义模板规则来自定义或“配置”该指令的行为。

    Will Template #2 execute? It matches a node in the source XML, but by the time the we get to this template (assuming it runs second), the "firstName" node will not be in the output tree.



    除非您有 <xsl:apply-templates/>,否则模板 #2(或任何其他模板规则)永远不会被触发。在 match="/" 中的某个地方打电话规则。如果您没有任何模板规则,那么除了 match="/" 之外没有其他模板规则。会被触发。可以这样想:要触发模板规则,它不能只匹配输入中的节点。它必须匹配您选择处理的节点(使用 <xsl:apply-templates/> )。相反,它将根据您选择处理它的次数继续匹配该节点。

    Would [the match="/" template] pre-empt all other templates from executing since there is nothing to match on after that first template is complete?



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

    To this point, I've been concerned with later templates not executing because the nodes they have operated on do not appear in the output, but what about the inverse? Can an "earlier" template create a node that a "later" template can do something with?



    并不是“更早的”模板创建了一个要处理的新节点;而是“更早的”模板使用相同的指令( <xsl:apply-templates )依次处理来自源树的更多节点。您可以将其视为递归调用相同的“函数”,每次使用不同的参数(要处理的节点由上下文和 select 属性决定)。

    最后,您得到的是对同一“函数”( <xsl:apply-templates> )的递归调用的树结构堆栈。而这个树结构是 同构到您的实际结果。不是每个人都意识到这一点或以这种方式思考过;那是因为我们还没有任何有效的可视化工具……还没有。

    Template #1 creates a new node called "fullName". Template #2 matches on that same node. Will Template #2 execute because the "fullName" node exists in the output by the time we get around to Template #2?



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

    无论您是处理原始源树中的节点还是您构造的临时树中的节点,无论哪种方式,您都需要明确说明要处理的节点。

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

    I realize that I'm deeply ignorant about the "zen" of XSLT. To date, my stylesheets have consisted of a template matching the root node, then are completely procedural from there. I'm tired of doing this. I would rather actually understand XSLT correctly, hence my question.



    我为你鼓掌。现在是时候服用“红色药丸”了:阅读 "How XSLT Works"