紧凑的XSLT代码,如果全部为空,则删除N个标记

时间:2010-03-24 12:06:24

标签: xml xslt

这是我的输入xml:

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4/>
   <othertags/>
</root>

输出必须是:

<root>
   <othertags/>
</root>

如果4个节点中的任何一个不为空,则必须删除所有标记。
示例:

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4>sample_text</node4>
   <othertags/>
</root>

然后输出必须与输入xml相同。

<root>
   <node1/>
   <node2/>
   <node3/>
   <node4>sample_text</node4>
   <othertags/>
</root>

这是我设计的XSL代码::

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
<xsl:template match="/root/node1[.='' and ../node2/.='' and ../node3/.='' and ../node4/.='']
             |/root/node2[.='' and ../node1/.='' and ../node3/.='' and ../node4/.='']
             |/root/node3[.='' and ../node1/.='' and ../node2/.='' and ../node4/.='']
             |/root/node4[.='' and ../node1/.='' and ../node2/.='' and ../node3/.='']"/>

正如您所看到的,代码需要更多的努力,并且随着节点数量的增加而变得更加庞大。有没有其他方法可以克服这个瓶颈?

4 个答案:

答案 0 :(得分:2)

您是否尝试过(未经测试)

<xsl:template match="node1|node2|node3|node4">
  <xsl:if test="
    (preceding-sibling::*|.|following-sibling::*)[
      self::node1 or self::node2 or self::node3 or self::node4
    ][.!='']
  ">
    <xsl:copy-of select="." />
  </if>
</xsl:template>

答案 1 :(得分:0)

模式“filter-that-nodes”检查条件,“not-those-nodes”删除不需要的节点。未过滤的节点将匹配其中一个无模式应用模板,就好像我们只是说'在'根'模板内部一样。

<xsl:template match="root">
    <xsl:copy>
        <xsl:apply-templates mode="filter-those-nodes"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*" mode="filter-those-nodes">
    <xsl:choose>
        <xsl:when test="'' = concat(node1, node2, node3, node4)">
            <xsl:apply-templates mode="filter-those-nodes"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:apply-templates/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="node1 | node2 | node3 | node4" mode="not-those-nodes">
    <!-- filtered out -->
</xsl:template>

<xsl:template match="*" mode="not-those-nodes">
    <xsl:apply-templates select="." />
</xsl:template>

答案 2 :(得分:0)

更精确,更优化的解决方案

<强>予。 “常规”XSLT 1.0           

 <xsl:variable name="vNotAllEmpty" select=
  "/*/*
      [self::node1|self::node2|self::node3|self::node4]
        [not(. = '')]

  "/>

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

 <xsl:template match=
   "/*/*/node1|/*/*/node2|/*/*/node3|/*/*/node4">
   <xsl:if test="$vNotAllEmpty">
     <xsl:call-template name="indent"/>
   </xsl:if>
 </xsl:template>
</xsl:stylesheet>
  1. 为什么更精确??因为特定模板仅匹配作为顶部元素的子节点的node{N}个节点。

  2. 为什么效率更高?因为决定是否处理node{N}节点的测试只执行一次,而每个节点发生一次。

  3. <强> II。使用FXSL

    FXSL library 具有方便的模板/函数,可以确定节点集中的所有节点或某些节点是否为真(或者对它们应用的条件是否为真)。 / p>

    对于这个问题,最合适的FXSL模板是someTrue这是完整的转换:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:import href="someTrue.xsl"/>
    
      <xsl:output omit-xml-declaration="yes"/>
    
        <xsl:variable name="vallNodes" select=
         "/*/*[starts-with(name(), 'node')]"/>
    
        <xsl:variable name="vNotAllEmpty">
         <xsl:call-template name="someTrue">
           <xsl:with-param name="pList"
              select="$vallNodes"/>
         </xsl:call-template>
        </xsl:variable>
    
     <xsl:template match="node()|@*" name="indent">
      <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
     </xsl:template>
    
     <xsl:template match="*">
       <xsl:variable name="visInspectedNode" select=
        "count(.|$vallNodes) = count($vallNodes)"/>
       <xsl:if test=
          "not($visInspectedNode)
          or
            ($visInspectedNode and string($vNotAllEmpty))
          ">
          <xsl:call-template name="indent"/>
       </xsl:if>
     </xsl:template>
    </xsl:stylesheet>
    

    请注意以下

    1. 要处理的节点(在$vallNodes中保留)可能有数百和数千个。

    2. 该解决方案具有高度可扩展性。我们可以分别使用FXSL模板allTruePsomeTrueP来测试所有节点或某些节点是否满足指定条件。

    3. 扫描所有节点并累积真值的逻辑是在这些模板中固定的,你从不编码这个逻辑 - 因此不会浪费额外的时间,也不可能提交错误。

答案 3 :(得分:0)

<xsl:template match="/root/node()[name()='node1' or name()='node2' or name()='node3' or name()='node4']
[../node1/.='' and ../node2/.='' and ../node3/.='' and ../node4/.='']"/>