XSLT - 使用 except 消除后代节点
Posted
技术标签:
【中文标题】XSLT - 使用 except 消除后代节点【英文标题】:XSLT - eliminate descendent node using except 【发布时间】:2016-10-25 03:24:15 【问题描述】:假设我有一个这样的示例 XML 文件,
<Entry>
<lang>
<definition>definition</definition>
<note>aaaaaaaaa
<list>
<list-item>aabbbbbbbb</list-item>
<list-item>aacccccccc</list-item>
<list-item>aadddddddd</list-item>
</list>
</note>
<example>bbbbbbbbbbb
<list>
<list-item>bbaaaaaaa</list-item>
<list-item>bbbbbbbbbbb</list-item>
<list-item>bbcccccccc</list-item>
</list>
</example>
</lang>
</Entry>
我已经写了这样的示例 xsl,
<xsl:template match="Entry">
<term>
<xsl:apply-templates select="node() except definition"/>
</term>
</xsl:template>
<xsl:template match="definition">
<para type="definition">
<xsl:apply-templates/>
</para>
</xsl:template>
<xsl:template match="lang">
<para type="Lang">
<xsl:apply-templates/>
</para>
</xsl:template>
<xsl:template match="example">
<para type="example">
<xsl:apply-templates/>
</para>
</xsl:template>
<xsl:template match="list">
<para type="list">
<xsl:apply-templates/>
</para>
</xsl:template>
<xsl:template match="list-item">
<para type="list-item">
<xsl:apply-templates/>
</para>
</xsl:template>
正如我在Entry
的模板中所写,我需要从输出中删除<definition>
内容。 (<xsl:apply-templates select="node() except definition"/>
)。
据我所知,我可以抑制 <xsl:template match="definition"/>
之类的元素并消除内容,但在我的实际场景中,我无法使用该方法。并且似乎except
关键字仅适用于给定节点的子元素,但不适用于上述示例的后代元素。
有什么建议可以在 xsl:template match="Entry"
的 apply-template 中消除 <definition>
吗? (不使用抑制definition
元素或使用不同的模式)
【问题讨论】:
为什么不能在真实场景中使用空模板?提供一些示例 XML,说明为什么这对您不起作用。 【参考方案1】:从你的 cmets 对你得到的(非常好的)答案来看,你显然对处理模型有很深的误解。
首先,xsl:apply-templates 选择一组节点,并使用每个节点的最佳匹配模板规则处理每个节点。
所以<apply-templates select="node()"/>
将模板应用于表达式node()
选择的所有节点,即上下文节点的所有子节点。
现在,表达式“node() except definition”选择了哪些节点?答:除了那些名为“定义”的子节点之外的所有子节点。由于没有名为“definition”的子节点,它选择与表达式“node()”完全相同的节点。
您试图做的是某种“远距离行动”:您试图说“以正常方式处理这些节点,但是当您这样做时,如果您在任何时候到达节点称为“定义”,然后忽略它“。 XSLT 不能以这种方式工作,任何其他编程语言也不能:您不能改变间接调用的某些函数或模板的效果,除非通过传递它可以理解的参数。最接近的方法是传递隧道参数,但它们只会修改显式编写以查找这些参数的模板的效果。
但这是实现您想要的一种过于复杂的方式。有两种方法可以确保不处理定义元素:
(a) 不要选择它们进行处理。您选择它们进行处理的位置来自带有 match="lang" 的模板,所以这就是您需要更改的地方。
(b) 确保如果它们被选中进行处理,则处理不会产生输出,这意味着更改 definition
元素的模板规则。
【讨论】:
【参考方案2】:好吧,definition
是 lang
的子级 - 所以如果你想消除它,你需要在从 lang
调用模板时这样做:
<xsl:template match="lang">
<para type="Lang">
<xsl:apply-templates select="node() except definition"/>
</para>
</xsl:template>
您现在将模板应用到所有属于Entry
子节点的节点,definition
除外。但由于definition
不是Entry
的子元素,所以异常没有意义。
似乎
except
关键字仅适用于给定节点的子元素 但不是上例中的后代元素。
不,except
适用于它之前的任何节点集。
补充:
但是即使我写
xsl:apply-templates select="node() except descendant::definition"
...<definition>
内容也不 消除?
当你写的时候:
<xsl:apply-templates select="node()">
这是以下的缩写:
<xsl:apply-templates select="child::node()">
添加except descendant::definition
是没有意义的。相当于:
A, B, C except D
如果你写的话,你会看到不同的结果:
<xsl:apply-templates select="descendant::node() except descendant::definition"/>
但这也会改变你的输出结构。如果要保留原始层次结构,则必须将模板从父级递归应用到子级 - 就像您所做的那样。
【讨论】:
感谢您的回答!但即使我写 xsl:apply-templates select="node() except descendant::definition" 而不是 xsl:apply-templates select="node() except definition" 在模板 match="Entry",以上是关于XSLT - 使用 except 消除后代节点的主要内容,如果未能解决你的问题,请参考以下文章