如何使用XSLT递归删除一些xml元素

时间:2010-04-14 15:04:02

标签: xml xslt recursion

所以我觉得这种情况很糟糕。我有这样的XML



<table border="1" cols="200 100pt 200">
  <tr>
    <td>isbn</td>
    <td>title</td>
    <td>price</td>
  </tr>
  <tr>
    <td />
    <td />
    <td>
      <span type="champsimple" id="9b297fb5-d12b-46b1-8899-487a2df0104e" categorieid="a1c70692-0427-425b-983c-1a08b6585364" champcoderef="01f12b93-b4c5-401b-9da1-c9385d77e43f">
        [prénom]
      </span>
      <span type="champsimple" id="e103a6a5-d1be-4c34-8a54-d234179fb4ea" categorieid="a1c70692-0427-425b-983c-1a08b6585364" champcoderef="01f12b93-b4c5-401b-9da1-c9385d77e43f">[nom]</span>
      <span></span>
    </td>
  </tr>
  <tr></tr>
  <tr>
    <td></td>
    <td>Phill It in</td>
  </tr>
  <tr>
    <table id="cas1">
      <tr>
        <td ></td>
        <td >foo</td>
      </tr>
      <tr>
        <td >bar</td>
        <td >boo</td>
      </tr>
    </table>
  </tr>
  <tr>
    <table id="cas2">
      <tr>
        <td ></td>
        <td >foo</td>
      </tr>
      <tr>
        <td ></td>
        <td >boo</td>
      </tr>
    </table>
  </tr>
  <tr>
    <table id="cas3">
      <tr>
        <td >bar</td>
        <td ></td>
      </tr>
      <tr>
        <td >foo</td>
        <td >boo</td>
      </tr>
    </table>
  </tr>
  <tr>
    <table id="cas4">
      <tr>
        <td />
        <td />
      </tr>
      <tr>
        <td>foo</td>
        <td>boo</td>
      </tr>
    </table>
  </tr>
  <table id="cas4">
    <tr>
      <td />
      <td />
    </tr>
    <tr>
      <td>foo</td>
      <td>boo</td>
    </tr>
  </table>
  <tr>
    <td />
    <td />
  </tr>
</table>


现在问题是如何递归删除所有空的td,tr和table元素?

现在我使用这个XSLT



<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*" />

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

  <xsl:template match="td[not(node())]" />
  <xsl:template match="tr[not(node())]" />
  <xsl:template match="table[not(node())]" />

</xsl:stylesheet>


但它做得并不好。删除td后,tr变为空,但不处理。太糟糕了。请使用“cas4”查看表格元素。



<table border="1" cols="200 100pt 200">
  <tr>
    <td>isbn</td>
    <td>title</td>
    <td>price</td>
  </tr>
  <tr>
    <td>
      <span type="champsimple" id="9b297fb5-d12b-46b1-8899-487a2df0104e" categorieid="a1c70692-0427-425b-983c-1a08b6585364" champcoderef="01f12b93-b4c5-401b-9da1-c9385d77e43f">
        [prénom]
      </span>
      <span type="champsimple" id="e103a6a5-d1be-4c34-8a54-d234179fb4ea" categorieid="a1c70692-0427-425b-983c-1a08b6585364" champcoderef="01f12b93-b4c5-401b-9da1-c9385d77e43f">[nom]</span>
      <span />
    </td>
  </tr>
  <tr>
    <td>Phill It in</td>
  </tr>
  <tr>
    <table id="cas1">
      <tr>
        <td>foo</td>
      </tr>
      <tr>
        <td>bar</td>
        <td>boo</td>
      </tr>
    </table>
  </tr>
  <tr>
    <table id="cas2">
      <tr>
        <td>foo</td>
      </tr>
      <tr>
        <td>boo</td>
      </tr>
    </table>
  </tr>
  <tr>
    <table id="cas3">
      <tr>
        <td>bar</td>
      </tr>
      <tr>
        <td>foo</td>
        <td>boo</td>
      </tr>
    </table>
  </tr>
  <tr>
    <table id="cas4">
      <tr />
      <tr>
        <td>foo</td>
        <td>boo</td>
      </tr>
    </table>
  </tr>
  <table id="cas4">
    <tr />
    <tr>
      <td>foo</td>
      <td>boo</td>
    </tr>
  </table>
  <tr />
</table>


您如何解决这个问题?

3 个答案:

答案 0 :(得分:4)

听起来你的空的定义是“不包含文本或只包含空白”。是这样的吗?如果是这样,以下转换应该可以解决问题:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
  <xsl:output omit-xml-declaration="yes" indent="yes"/> 
  <xsl:strip-space elements="*" /> 

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

  <xsl:template match="td[not(normalize-space(.))]" /> 
  <xsl:template match="tr[not(normalize-space(.))]" /> 
  <xsl:template match="table[not(normalize-space(.))]" /> 
</xsl:stylesheet> 

答案 1 :(得分:1)

您还可以过滤掉任何仅包含&lt; tr&gt;的表格。空的&lt; td&gt;,以及任何&lt; tr&gt;只有空&lt; tr&gt; (除了你的其他过滤器),使用这样的东西(未测试):

<xsl:template match="tr[not(td/node())]" /> 
<xsl:template match="table[not(tr/td/node())]" /> 

答案 2 :(得分:1)

有你的解决方案:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

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

    <xsl:template match="@* | text()">
        <xsl:copy />
    </xsl:template>

    <xsl:template match="table | tr | td">

        <!-- result of the transformation of descendants -->
        <xsl:variable name="content">
            <xsl:apply-templates select="node()" />
        </xsl:variable>

        <!-- if there are any children left then copy myself -->
        <xsl:if test="count($content/node()) > 0">
            <xsl:copy>
                <xsl:apply-templates select="@*" />
                <xsl:copy-of select="$content" />
            </xsl:copy>
        </xsl:if>

    </xsl:template>

</xsl:stylesheet>

这个想法很简单。我会先为我的后代进行改造,然后我会看看是否还有人离开。如果是这样,我会复制自己和转化的结果。

如果要保留表结构并仅删除空行 - 仅包含空元素<tr>的元素<td>,而不仅仅为<tr>创建具有不同条件的类似模板并忽略元素<td>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

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

    <xsl:template match="@* | text()">
        <xsl:copy />
    </xsl:template>

    <xsl:template match="table">

        <!-- result of the transformation of descendants -->
        <xsl:variable name="content">
            <xsl:apply-templates select="node()" />
        </xsl:variable>

        <!-- if there are any children left then copy myself -->
        <xsl:if test="count($content/node()) > 0">
            <xsl:copy>
                <xsl:apply-templates select="@*" />
                <xsl:copy-of select="$content" />
            </xsl:copy>
        </xsl:if>

    </xsl:template>

    <xsl:template match="tr">

        <!-- result of the transformation of descendants -->
        <xsl:variable name="content">
            <xsl:apply-templates select="node()" />
        </xsl:variable>

        <!-- number of non-empty td elements -->
        <xsl:variable name="cellCount">
            <xsl:value-of select="count($content/td[node()])" />
        </xsl:variable>

        <!-- number of other elements -->
        <xsl:variable name="elementCount">
            <xsl:value-of select="count($content/node()[name() != 'td'])" />
        </xsl:variable>

        <xsl:if test="$cellCount > 0 or $elementCount > 0">
            <xsl:copy>                  
                <xsl:apply-templates select="@*" />
                <xsl:copy-of select="$content" />
            </xsl:copy>
        </xsl:if>

    </xsl:template>

</xsl:stylesheet>

嗯,实际上最后一个if应该是这样的:

<xsl:choose>
    <!-- if there are cells then copy the content -->
    <xsl:when test="$cellCount > 0">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:copy-of select="$content" />
        </xsl:copy>
    </xsl:when>

    <!-- if there are only other elements copy them -->
    <xsl:when test="$elementCount > 0">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:copy-of select="$content/node()[name() != 'td']" />
        </xsl:copy>
    </xsl:when>
</xsl:choose>

这是因为<tr>包含空元素<td>和其他元素的情况。然后,您要删除<td>并仅保留其余内容。