本文介绍了XSLT 文档中的模板以什么顺序执行,它们是否与源 XML 或缓冲输出匹配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是关于 XSLT 一直让我感到困惑的地方:

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

示例:

<firstName>Deane</firstName><lastName>Barker</lastName></人>

这是 XSLT 的一个片段:

<xsl:template match="/"><xsl:value-of select="firstName"/><xsl:value-of select="lastName"/></xsl:模板><!-- 模板 #2 --><xsl:template match="/person/firstName">名字:<xsl:value-of select="firstName"/></xsl:模板>

关于此的两个问题:

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

那么,后来的"模板是受制于较早的"模板中发生的事情,还是对源文档进行操作,而忽略了之前"发生的变化?(所有这些词都用引号引起来,因为我发现很难讨论基于时间的问题,因为我真的不知道最初是如何确定模板顺序的......)

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

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

在与上面相同的 XML 上,考虑这个 XSL:

<xsl:template match="/"><xsl:value-of select="firstName"/><xsl:value-of select="lastName"/></全名></xsl:模板><!-- 模板 #2 --><xsl:template match="//fullName">全名:<xsl:value-of select="."/></xsl:模板>

模板 #1 创建一个名为fullName"的新节点.模板 #2 在同一节点上匹配.模板 #2 是否会因为fullName"节点在我们处理模板 #2 时存在于输出中而执行?

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

解决方案

我喜欢你的问题.你对你还不明白的东西非常清楚.你只需要一些东西来把事情联系在一起.我的建议是您阅读 "How XSLT Works",这是我写的一章,专门针对你问的问题.我很想听听它是否为您将事情联系在一起.

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

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

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

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

我假设模板 #1 将先执行.我不知道为什么我假设这个——是不是仅仅因为它出现在文档中的第一个?

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

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

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

将 [match="/"模板] 抢占所有其他模板从执行,因为什么都没有在第一个模板之后匹配完成了吗?

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

到目前为止,我一直很担心以后的模板没有执行因为他们运营的节点on 不会出现在输出中,但是反过来呢?能不能较早的"模板创建一个节点稍后"模板可以做一些事情与?

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

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

模板 #1 创建一个名为的新节点全名".模板 #2 匹配同一个节点.将模板 #2执行因为fullName"节点当我们在输出中存在时绕过模板#2?

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

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

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

我意识到我非常无知关于 XSLT 的禅".迄今为止,我的样式表由一个模板匹配根节点,然后从那里完全程序化.我厌倦了这样做.我会而是真正理解 XSLT正确,因此我的问题.

我为你鼓掌.现在是服用红色药丸"的时候了:阅读XSLT 的工作原理"

Here is something that has always mystified me about XSLT:

  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?

Example:

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

Here is a fragment of 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>

Two questions about this:

  1. 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?
  2. 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.

So, are "later" templates beholden to what has occurred in "earlier" templates, or do they operate on the source document, oblivious to what has been transformed "prior" to them? (All those words are in quotes, because I find it hard to discuss time-based issues when I really have little idea how template order is determined in the first place...)

In the above example, we have a template that matches on the root node ("/") that -- when it is done executing -- has essentially removed all nodes from the output. This being the case, would this pre-empt all other templates from executing since there is nothing to match on after that first template is complete?

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?

On the same XML as above, consider this 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>

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?

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.

解决方案

I love your question. You're very articulate about what you do not yet understand. You just need something to tie things together. My recommendation is that you read "How XSLT Works", a chapter I wrote to address exactly the questions you're asking. I'd love to hear if it ties things together for you.

Less formally, I'll take a stab at answering each of your questions.

At any given point in XSLT processing, there are, in a sense, two contexts, which you identify as (a) and (b): where you are in the source tree, and where you are in the result tree. Where you are in the source tree is called the current node. It can change and jump all around the source tree, as you choose arbitrary sets of nodes to process using XPath. However, conceptually, you never "jump around" the result tree in the same way. The XSLT processor constructs it in an orderly fashion; first it creates the root node of the result tree; then it adds children, building the result in document order (depth-first). [Your post motivates me to pick up my software visualization for XSLT experiments again...]

The order of template rules in a stylesheet never matters. You can't tell, just by looking at the stylesheet, in what order the template rules will be instantiated, how many times a rule will be instantiated, or even whether it will be at all. (match="/" is an exception; you can always know that it will get triggered.)

Nope. It would be called first even if you put it last in the document. Template rule order never matters (except under an error condition when you have more than one template rule with the same priority matching the same node; even then, it's optional for the implementor and you should never rely on such behavior). It gets called first because the first thing that always happens whenever you run an XSLT processor is a virtual call to <xsl:apply-templates select="/"/> . The one virtual call constructs the entire result tree. Nothing happens outside it. You get to customize, or "configure", the behavior of that instruction by defining template rules.

Template #2 (nor any other template rules) will never get triggered unless you have an <xsl:apply-templates/> call somewhere in the match="/" rule. If you don't have any, then no template rules other than match="/" will get triggered. Think of it this way: for a template rule to get triggered, it can't just match a node in the input. It has to match a node that you elect to process (using <xsl:apply-templates/>). Conversely, it will continue to match the node as many times as you choose to process it.

That rule preempts the rest by nowhere including <xsl:apply-templates/> in it. There are still plenty of nodes that could be processed in the source tree. They're always all there, ripe for the picking; process each one as many times as you want. But the only way to process them using template rules is to call <xsl:apply-templates/>.

It's not that an "earlier" template creates a new node to be processed; it's that an "earlier" template in turn processes more nodes from the source tree, using that same instruction (<xsl:apply-templates). You can think of it as calling the same "function" recursively, with different parameters each time (the nodes to process as determined by the context and the select attribute).

In the end, what you get is a tree-structured stack of recursive calls to the same "function" (<xsl:apply-templates>). And this tree structure is isomorphic to your actual result. Not everyone realizes this or has thought about it this way; that's because we don't have any effective visualization tools...yet.

Nope. The only way to do a chain of processing is to explicitly set it up that way. Create a variable, e.g., $tempTree, that contains the new <fullName> element and then process it, like this <xsl:apply-templates select="$tempTree">. To do this in XSLT 1.0, you need to wrap the variable reference with an extension function (e.g., exsl:node-set()), but in XSLT 2.0 it will work just as is.

Whether you're processing nodes from the original source tree or in a temporary tree that you construct, either way you need to explicitly say what nodes you want to process.

What we haven't covered is how XSLT gets all its implicit behavior. You must also understand the built-in template rules. I write stylesheets all the time that don't even include an explicit rule for the root node (match="/"). Instead, I rely on the built-in rule for root nodes (apply templates to children), which is the same as the built-in rule for element nodes. Thus I can ignore large parts of the input, let the XSLT processor automatically traverse it, and only when it comes across a node I'm interested in will I do something special. Or I could write a single rule that copies everything recursively (called the identity transform), overriding it only where necessary, to make incremental changes to the input. After you've read "How XSLT Works", your next assignment is to look up the "identity transform".

I applaud you. Now it's time to take the "red pill": read "How XSLT Works"

这篇关于XSLT 文档中的模板以什么顺序执行,它们是否与源 XML 或缓冲输出匹配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-28 11:21