42

我阅读了一些 XSLT 示例并找到了该代码:

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

这意味着什么?

4

3 回答 3

52

XPath 表达式@* | node()选择属性节点 ( ) 和所有其他类型的 XML 节点 ( ) 的并@*node()

它是 的简写attribute::* | child::node()

在 XSLT 中,XPath 是相对于上下文节点的,默认选择child轴,所以表达式

  • 选择上下文节点的所有属性和直接子节点(当用作select="..."表达式时,例如 in <xsl:apply-templates>
  • 匹配所有属性和其他节点,而不考虑上下文(当用作 中的match=""表达式时<xsl:template>) - 请注意,选择节点和匹配它们之间存在差异:上下文节点仅对选择很重要。

想象以下节点是上下文节点:

<xml attr="value">[
  ]<child />[
  ]<!-- comment -->[
  ]<child>
    <descendant />
  </child>[
]</xml>

该表达式node()不仅会选择两个<child>节点,还会选择四个纯空格文本节点(由可见性表示)和注释[]<descendant>选中。

XML 的一个特殊特性是属性节点不是它们所属元素的子节点(尽管属性的父节点是它所属的元素)。

这种不对称关系使得有必要分别选择它们,因此@*.

它匹配属于上下文节点的任何属性节点,因此attr="value"也将被选中。

|XPath 联合运算符。它从两个单独的节点集创建单个节点集。

<xsl:apply-templates>然后<xsl:template>为每个选定的节点找到合适的并为该节点运行它。这就是我上面提到的模板匹配部分。

于 2012-06-23T06:56:09.873 回答
22

要添加到 Tomalak 的出色答案:

大多数情况下,人们会看到<xsl:apply-template select="@*|node()"/>在这样的模板中使用

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

这称为身份规则或“身份模板”。

最基本和最强大的 XSLT 设计模式之一是标识规则的使用和覆盖

如果转换仅包含身份规则,则转换的结果是源 XML 文档本身——这就是模板被称为“身份规则”的原因。

为什么会产生这个结果?

简短的回答是:因为 XSLT 处理模型。

更详细的解释必须从顶部开始

node()

匹配任何元素、文本节点、注释或处理指令。文档-(根)-节点也与 匹配node()

我们可以想象任何文档树的“叶”节点——这些节点本身没有子节点,例如文本节点、注释和处理指令。空元素也应被视为叶节点。

身份规则最初被选择用于对文档节点的所有子节点执行(应用)(这些是单个顶部元素和它可能具有的任何注释或处理指令兄弟)。匹配的节点是浅拷贝的,如果它是非元素叶节点,则<xsl:apply-templates select="node()|@*"/>指令不会选择任何节点或属性。

如果匹配的节点是一个元素,那么它是浅拷贝的,那么<xsl:apply-templates select="node()|@*"/>指令会导致将相同的模板(因为在转换代码中没有任何其他模板)应用于其每个属性及其每个子节点。

这是驱动处理 XML 文档的每个节点的递归,直到到达叶节点或属性,并且在该位置不<xsl:apply-templates select="node()|@*"/>选择子节点或属性节点。

于 2012-06-23T14:31:18.473 回答
9

恭喜@Tomalak 获得第一个正确答案。打勾应该是他的回答。我只想对他的回答进行一些澄清。

注一

... @* | node() 选择...的并集

该| 运算符不仅返回两个操作数的并集,而且按文档顺序排序并删除重复项。de-dup 部分在这里不相关,因为没有要删除的重复项,但排序部分值得注意。更正确的版本是说...

... @* | node() 选择联合,按文档顺序排序,...

注二

...以及所有其他类型的 XML 子节点 (node())

这大体上是正确的,但具有误导性。当大多数人阅读“XML 子节点”时,他们会想到 DOM 意义上的子节点。但这不是被选中的。仅选择 XDM 节点。有关说明,请查看以下文档。

<?xml version="1.0" encoding="ISO-8859-1"?>
<root-element my-attrib="myattrib-vaue" xmlns:hi="www.abc.com"><child-element />
abc&apos;def
</root-element>

现在假设上下文项是“根元素”。Tomalak 的回答的读者被问到一个问题:“@*|node()”选择了什么?对于那些思考 DOM 模型的人来说,Tomalak 的答案意味着选择了 6 件事:

  • my-attrib 属性
  • 节点空间属性(这是 DOM 中的真实属性)
  • 子元素节点
  • “abc”位
  • 实体参考
  • “def”位。

但在 XSLT 中这实际上并非如此。实际选择的是...

  • my-attrib 属性
  • 子元素节点
  • XDM 文本节点,是 3 个 DOM 文本节点的连接,例如:“abc'def”

所以更准确的说法是......

XPath 表达式@* | node() 选择按文档顺序排序的联合(上下文项的属性节点和 XDM 意义上的上下文项的 XML 子节点)。XD 模型忽略了 DOM 中的一些节点类型,例如实体定义,并且将连续的文本 DOM 节点连接成一个 XDM 文本节点。

于 2012-06-24T04:58:55.480 回答