使用XSLT基于属性删除元素

时间:2013-05-31 12:54:36

标签: xslt

当条目中存在属性时,我需要隐藏元素。当存在属性cols时,其余的空条目应隐藏在同一行中。当存在属性morerows时,应删除存在于同一列的下一行中的条目。

示例输入:

<?xml version="1.0"?>
<table>
<tbody>
<row>
<entry cols="2">Row 1 Col 1</entry>
<entry></entry>
<entry></entry>
<entry morerows="2">Row 1 Col 4</entry>
<entry>Row 1 Col 5</entry>
</row>
<row>
<entry>Row 2 Col 1</entry>
<entry>Row 2 Col 2</entry>
<entry>Row 2 Col 3</entry>
<entry></entry>
<entry>Row 2 Col 5</entry>
</row>
<row>
<entry>Row 3 Col 1</entry>
<entry>Row 3 Col 2</entry>
<entry>Row 3 Col 3</entry>
<entry></entry>
<entry>Row 3 Col 5</entry>
</row>
</tbody>
</table>

输出:

<?xml version="1.0"?>
<table>
<tbody>
<row>
<entry cols="2">Row 1 Col 1</entry>
<entry morerows="2">Row 1 Col 4</entry>
<entry>Row 1 Col 5</entry>
</row>
<row>
<entry>Row 2 Col 1</entry>
<entry>Row 2 Col 2</entry>
<entry>Row 2 Col 3</entry>
<entry>Row 2 Col 5</entry>
</row>
<row>
<entry>Row 3 Col 1</entry>
<entry>Row 3 Col 2</entry>
<entry>Row 3 Col 3</entry>
<entry>Row 3 Col 5</entry>
</row>
</tbody>
</table>

XSLT尝试过:

<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML">
<xsl:output method="xml" encoding="UTF-8" indent="no"/>

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

<xsl:template match="row">
<xsl:copy>
<xsl:for-each select="entry">
<xsl:if test="@cols"><xsl:variable name="span_cols" select="@cols+1"/></xsl:if>
<xsl:apply-templates select="following-sibling::*[$span_cols]"/>
</xsl:for-each>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>


<xsl:template match="entry">
<xsl:choose>
<xsl:when test="@cols">
<xsl:copy>
<xsl:variable name="current_colno" select="count(preceding-sibling::entry)+1"/>
<xsl:variable name="span_cols" select="@cols+1"/>
<xsl:attribute name="namest"><xsl:value-of select="$current_colno"/></xsl:attribute>
<xsl:attribute name="nameend"><xsl:value-of select="sum($span_cols,$current_colno)"/></xsl:attribute>
</xsl:copy>
</xsl:when>
<xsl:otherwise><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:otherwise>
</xsl:choose>
</xsl:template>

</xsl:stylesheet

&GT;

1 个答案:

答案 0 :(得分:1)

我编写了一些代码,试图分三步实现转换:

  1. 标记由于entry属性
  2. 而要删除的cols个元素
  3. 标记由于entry属性
  4. 而要删除的morerows个元素
  5. 最后删除已标记的entry元素
  6. 以下是代码:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="xs">
    
    <xsl:output indent="yes"/>
    
    <xsl:template match="@* | node()" mode="#all">
      <xsl:copy>
        <xsl:apply-templates select="@* , node()" mode="#current"/>
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="tbody">
      <xsl:copy>
        <xsl:variable name="cols-flagged-to-delete" as="element(tbody)">
          <xsl:copy>
            <xsl:apply-templates select="*" mode="flag-to-delete-col"/>
          </xsl:copy>
        </xsl:variable>
        <xsl:variable name="rows-flagged-to-delete" as="element(row)*">
          <xsl:apply-templates select="$cols-flagged-to-delete" mode="flag-to-delete-row"/>
        </xsl:variable>
        <xsl:apply-templates select="$rows-flagged-to-delete" mode="delete"/>
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="tbody/row" mode="flag-to-delete-col">
      <xsl:copy>
        <xsl:for-each-group select="entry" group-starting-with="entry[@cols]">
          <xsl:choose>
            <xsl:when test="self::entry[@cols]">
              <xsl:variable name="cols" as="xs:integer" select="xs:integer(@cols)"/>
              <xsl:apply-templates select="."/>
              <xsl:apply-templates select="current-group()[not(node()) and position() gt 1 and position() le (1 + $cols)]"
                 mode="flag-to-delete-col"/>
              <xsl:apply-templates select="current-group()[position() gt (1 + $cols)]"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:apply-templates select="current-group()"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:for-each-group>
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="tbody/row/entry" mode="flag-to-delete-col flag-to-delete-row">
      <entry delete="true"/>
    </xsl:template>
    
    <xsl:template match="tbody" mode="flag-to-delete-row">
      <xsl:for-each-group select="row" group-starting-with="row[entry/@morerows]">
        <xsl:choose>
          <xsl:when test="self::row[entry/@morerows]">
            <xsl:variable name="pos" as="xs:integer" select="count(entry[@morerows]/preceding-sibling::entry) + 1"/>
            <xsl:variable name="n" as="xs:integer" select="xs:integer(entry/@morerows)"/>
            <xsl:apply-templates select="current-group()" mode="flag-to-delete-row">
              <xsl:with-param name="pos" select="$pos"/>
              <xsl:with-param name="n" select="$n"/>
            </xsl:apply-templates>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="current-group()"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    </xsl:template>
    
    <xsl:template match="tbody/row" mode="flag-to-delete-row">
      <xsl:param name="pos"/>
      <xsl:param name="n"/>
      <xsl:copy>
        <xsl:choose>
          <xsl:when test="position() gt 1 and position() le (1 + $n)">
            <xsl:apply-templates select="entry[position() lt $pos]"/>
            <xsl:apply-templates select="entry[$pos]" mode="flag-to-delete-row"/>
            <xsl:apply-templates select="entry[position() gt $pos]"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:apply-templates select="entry"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="row/entry[@delete = 'true']" mode="delete"/>
    
    </xsl:stylesheet>
    

    但是我只测试了你提供的小样本,所以当我使用Saxon 9.5进行转换时

    <?xml version="1.0"?>
    <table>
      <tbody>
        <row>
          <entry cols="2">Row 1 Col 1</entry>
          <entry></entry>
          <entry></entry>
          <entry morerows="2">Row 1 Col 4</entry>
          <entry>Row 1 Col 5</entry>
        </row>
        <row>
          <entry>Row 2 Col 1</entry>
          <entry>Row 2 Col 2</entry>
          <entry>Row 2 Col 3</entry>
          <entry></entry>
          <entry>Row 2 Col 5</entry>
        </row>
        <row>
          <entry>Row 3 Col 1</entry>
          <entry>Row 3 Col 2</entry>
          <entry>Row 3 Col 3</entry>
          <entry></entry>
          <entry>Row 3 Col 5</entry>
        </row>
      </tbody>
    </table>
    

    我确实得到了

    <table>
      <tbody>
          <row>
             <entry cols="2">Row 1 Col 1</entry>
             <entry morerows="2">Row 1 Col 4</entry>
             <entry>Row 1 Col 5</entry>
          </row>
          <row>
             <entry>Row 2 Col 1</entry>
             <entry>Row 2 Col 2</entry>
             <entry>Row 2 Col 3</entry>
             <entry>Row 2 Col 5</entry>
          </row>
          <row>
             <entry>Row 3 Col 1</entry>
             <entry>Row 3 Col 2</entry>
             <entry>Row 3 Col 3</entry>
             <entry>Row 3 Col 5</entry>
          </row>
       </tbody>
    </table>
    

    但我认为需要更复杂的输入和输出样本来测试代码,所以请这样做并报告回来。我假设每个morerows元素的entry元素中只有一个row属性,我不确定这个假设适用于您的输入要求。