使用XPath,如果选择了所有子项,则选择父项

时间:2018-02-19 12:33:19

标签: xml xslt xpath xslt-2.0 xpath-2.0

我有一个删除元素的样式表(XSLT 2.0)。现在我遇到一个问题,即我正在“修剪”的XML的DTD不允许我删除某个节点下的所有元素,也不会删除空节点。因此,如果删除所有子项,我也想删除父元素。我想用XPath表达式选择要删除的元素。

作为一个例子,考虑这个XML(没有提供DTD,但基本上说明一个盒子必须包含至少一个蜡笔):

<?xml version="1.0" encoding="UTF-8"?>
<test>
    <box>
        <crayon color="red"/>
        <crayon color="red"/>
        <crayon color="red"/>
        <crayon/>
    </box>
    <box>
        <crayon/>
        <crayon/>
    </box>
    <box>
        <crayon color="red"/>
        <crayon color="red"/>
    </box>
    <box>
        <crayon color="red"/>
        <crayon color="red"/>
        <crayon color="red"/>
        <crayon/>
    </box>
</test>

我想要的输出如下:

<?xml version="1.0" encoding="UTF-8"?>
<test>
    <box>
        <crayon/>
    </box>
    <box>
        <crayon/>
        <crayon/>
    </box>
    <box>
        <crayon/>
    </box>
</test>

这是一个样式表,遗憾的是没有做我想要的,但显示了我想要实现的形式:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*" />

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

    <!-- Next row should apply to sets of crayons or complete boxes. -->
    <xsl:template match="//box[if (count(crayon[@color = 'red']) = count(crayon)) then (.) else (crayon[@color = 'red'])]"/>

</xsl:stylesheet>

我想使用一个XPath表达式管理它的原因是我有一个生成样式表的函数,将XPath作为输入参数。

1 个答案:

答案 0 :(得分:1)

XSLT 3(由Saxon 9.8或Altova 2017或2018或Exselt支持)是一种选择吗?在那里,您可以利用新的xsl:where-populatedhttps://www.w3.org/TR/xslt/#element-where-populated):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0">

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="box">
      <xsl:where-populated>
          <xsl:next-match/>
      </xsl:where-populated>
  </xsl:template>

  <xsl:template match="box/crayon[@color = 'red']"/>

</xsl:stylesheet>

http://xsltfiddle.liberty-development.net/6qM2e2g

我不太确定你需要为参数或变量设置哪个部分,但是具有浅属性的XSLT 3也可以简化该任务。

使用XSLT 2我认为你可以使用

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">


  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

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

    <xsl:template match="box[not(crayon[not(@color = 'red')])] | box/crayon[@color = 'red']"/>

</xsl:transform>

http://xsltransform.hikmatu.com/nbUY4kp