首页 > 解决方案 > 满足复杂条件时的模板覆盖

问题描述

我有一些在语义上类似于以下的 XML:

<root>
  <parent>
    <a>
      <b t="0"/>
    </a>
    <a>
      <b t="0"/>
    </a>
    <a so="2">
      <b t="1">
        <c n="x"/>
      </b>
    </a>    
    <a>
      <b t="2">
        <c n="x"/>
      </b>
    </a>
    <a>
      <b t="1">
        <c n="y"/>
      </b>
    </a>
    <a so="3">
      <b t="2">
        <c n="z"/>
      </b>
   </a>
  </parent>
  <parent>
    <a so="1">
      <b t="2">
        <c n="x"/>
      </b>
    </a>
    <a so="4">
      <b t="1">
        <c n="z"/>
      </b>
    </a>
  </parent>
</root>

我有一个适用于所有a元素的模板:

<xsl:template match="a" mode="whatever">
  do some nice stuff with the content of a
</xsl:template>

如果满足某些条件,我有一些模板会覆盖行为以不执行任何操作。例如:

<xsl:template match="a[@t='0'][preceding-sibling:a[@t='0']" mode="whatever">
<!-- ignore this node entirely -->
</xsl:template>

(注意在上面的例子中,文件顺序很重要,在下面的问题中不重要)

效果很好,但是现在我需要做一些更复杂的事情。a鉴于满足以下条件,我想忽略上述示例 xml 中的第 4 个元素:

在示例 xml 中,最后一个节点不应与此规则匹配,因为 @n 的值不同。

我不在乎a节点的顺序,或者中间是否有其他东西。但是,如果需要,我可以声明节点将按照显示的顺序排列,没有中间节点。

除了模板覆盖之外,可能还有其他方法可以实现这一点,但是为了代码整洁,我真的在寻找基于模板的解决方案,如果存在的话。

我目前正在使用不支持 XSLT2.0 或 XPATH2.0 的 xmlstarlet,我可以研究其他引擎,但如果可能的话,我更愿意坚持使用我所拥有的引擎。

NB 不同节点中的parent节点不应相互影响 - 例如,标记为的节点@so=1不应受到标记为的节点的存在影响,@so=2因为它们位于不同的父节点中。对于@so=3和 也是如此@so=4。在示例中,这些节点都不应该匹配

标签: xsltxpathxslt-1.0

解决方案


以下 XSLT 片段a根据要求删除了第 4 个:

<!-- all a keyed by the value of b/@t -->
<xsl:key name="k1" match="//parent/a" use="b/@t"/>

<xsl:template match="//parent/a[b/@t = 2 and
                     key('k1', 1)/b/c/@n = b/c/@n]"/>

match 子句选择所有a具有b/@t = 2. 然后它检查是否存在与当前元素具有相同值的awhere 。b/@t = 1b/c/@n

如果检查应该只发生在同一个父级中,您可以将父级的 ID 添加到键中,如下所示:

<!-- all a keyed by parent node and the value of b/@t -->
<xsl:key name="k1" match="//parent/a" use="concat(generate-id(..),'|',b/@t)"/>

<xsl:template match="//parent/a[b/@t = 2 and
                     key('k1', concat(generate-id(..),'|',1))/b/c/@n = b/c/@n]"/>

后一个 XSLT 脚本转换以下 XML:

<root>
  <parent>
    <a>       <b t="0"/></a>
    <a>       <b t="0"/></a>
    <a so="2"><b t="1"><c n="x"/></b></a>
    <a>       <b t="2"><c n="x"/></b></a>
    <a>       <b t="1"><c n="y"/></b></a>
    <a so="3"><b t="2"><c n="z"/></b></a>
  </parent>
  <parent>
    <a so="1"><b t="2"><c n="x"/></b></a>
    <a so="4"><b t="1"><c n="z"/></b></a>
  </parent>
</root>

进入

<root>
  <parent>
    <a>       <b t="0"/></a>
    <a>       <b t="0"/></a>
    <a so="2"><b t="1"><c n="x"/></b></a>

    <a>       <b t="1"><c n="y"/></b></a>
    <a so="3"><b t="2"><c n="z"/></b></a>
  </parent>
  <parent>
    <a so="1"><b t="2"><c n="x"/></b></a>
    <a so="4"><b t="1"><c n="z"/></b></a>
  </parent>
</root>

推荐阅读