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 的模板中所写,我需要从输出中删除&lt;definition&gt; 内容。 (&lt;xsl:apply-templates select="node() except definition"/&gt;)。

据我所知,我可以抑制 &lt;xsl:template match="definition"/&gt; 之类的元素并消除内容,但在我的实际场景中,我无法使用该方法。并且似乎except 关键字仅适用于给定节点的子元素,但不适用于上述示例的后代元素。

有什么建议可以在 xsl:template match="Entry" 的 apply-template 中消除 &lt;definition&gt; 吗? (不使用抑制definition元素或使用不同的模式)

【问题讨论】:

为什么不能在真实场景中使用空模板?提供一些示例 XML,说明为什么这对您不起作用。 【参考方案1】:

从你的 cmets 对你得到的(非常好的)答案来看,你显然对处理模型有很深的误解。

首先,xsl:apply-templates 选择一组节点,并使用每个节点的最佳匹配模板规则处理每个节点。

所以&lt;apply-templates select="node()"/&gt; 将模板应用于表达式node() 选择的所有节点,即上下文节点的所有子节点。

现在,表达式“node() except definition”选择了哪些节点?答:除了那些名为“定义”的子节点之外的所有子节点。由于没有名为“definition”的子节点,它选择与表达式“node()”完全相同的节点。

您试图做的是某种“远距离行动”:您试图说“以正常方式处理这些节点,但是当您这样做时,如果您在任何时候到达节点称为“定义”,然后忽略它“。 XSLT 不能以这种方式工作,任何其他编程语言也不能:您不能改变间接调用的某些函数或模板的效果,除非通过传递它可以理解的参数。最接近的方法是传递隧道参数,但它们只会修改显式编写以查找这些参数的模板的效果。

但这是实现您想要的一种过于复杂的方式。有两种方法可以确保不处理定义元素:

(a) 不要选择它们进行处理。您选择它们​​进行处理的位置来自带有 match="lang" 的模板,所以这就是您需要更改的地方。

(b) 确保如果它们被选中进行处理,则处理不会产生输出,这意味着更改 definition 元素的模板规则。

【讨论】:

【参考方案2】:

好吧,definitionlang 的子级 - 所以如果你想消除它,你需要在从 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" ... &lt;definition&gt; 内容也不 消除?

当你写的时候:

<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", content不消除? @sanjay 查看我的答案的补充。 @sanjay 这个问题没有回答吗?

以上是关于XSLT - 使用 except 消除后代节点的主要内容,如果未能解决你的问题,请参考以下文章

xslt copy-of到结果文档中的不同命名空间

XSLT 转换以消除嵌套

使用 XSLT 添加强制节点

使用 xslt 将 xml 复杂节点元素拆分为多个节点

使用 XSLT 检查节点是不是存在

使用 XSLT 过滤不包含节点的 XYZ