XSLT处理多个节点

时间:2012-10-24 09:12:23

标签: xslt

我有以下XML,它是一个12项管道分隔值。我只是为了解释而添加了空白节点中的空格,其他方面也是不存在的。

<Base>
  <Span>a|a| |a| |a| | |a| |a|a</Span>
  <Span>b| | |b| | |b| | | | |b</Span>
  <Span> | | | |c| | |c| | |c| </Span>
</Base>

我希望从中获得的输出如下:

<Output>
  <Period>a|b</Period>
  <Period>a</Period>
  <Period>a|b</Period>
  <Period>c</Period>
  <Period>a</Period>
  <Period>b</Period>
  <Period>c</Period>
  <Period>a</Period>
  <Period>a|c</Period>
  <Period>a|b</Period>
</Output>

这是通过检查所有跨度中的每列是否具有值来实现的,如果任何跨度在该列中具有创建条目的值。例如,跨度中的第一列包含a, b, -,因此输出变为<Period>a|b</Period>

我正在使用XSLT 1.0,Span中的值可能是任何东西(a,b和c似乎很简单,可以解释)。

我不完全确定如何处理这个问题。

3 个答案:

答案 0 :(得分:1)

此XSLT 1.0样式表......

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exsl="http://exslt.org/common"
  xmlns:so="http://stackoverflow.com/questions/13046157"
  exclude-result-prefixes="xsl exsl so">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />

<xsl:template match="/*">
  <Output>
    <xsl:variable name="particles">
      <xsl:apply-templates select="Span" />
    </xsl:variable>
    <xsl:variable name="col-count">
      <xsl:for-each select="exsl:node-set($particles)/so:particle">
        <xsl:sort select="@col" data-type="number" order="descending"/>
        <xsl:if test="position()=1">
          <xsl:value-of select="@col"/>
        </xsl:if>
      </xsl:for-each>
    </xsl:variable>
    <xsl:for-each select="exsl:node-set($particles)/so:particle[position() &lt;= $col-count]">
      <xsl:variable name="col" select="position()" />
      <xsl:variable name="period">
        <xsl:for-each select="exsl:node-set($particles)/so:particle[@col=$col]">
          <xsl:sort select="@row" data-type="number" order="ascending"/>
          <xsl:if test=". != ''">
            <xsl:value-of select="." />
            <xsl:if test="position() != last()">
              <xsl:value-of select="'|'" />
            </xsl:if>  
          </xsl:if>  
        </xsl:for-each>
      </xsl:variable>
      <xsl:if test="$period != ''">
        <Period><xsl:value-of select="$period" /></Period>
      </xsl:if>
    </xsl:for-each>
  </Output>
</xsl:template>

<xsl:template match="Span" name="span">
  <xsl:param name="span-text" select="." />
  <xsl:param name="row" select="position()" />
  <xsl:param name="col" select="1" />
  <xsl:variable name="part" select="
    normalize-space( substring-before( concat( $span-text, '|'), '|'))" />
  <xsl:if test="$part">
    <so:particle row="{$row}" col="{$col}">
      <xsl:value-of select="$part" />
    </so:particle>
  </xsl:if>  
  <xsl:if test="contains($span-text,'|')">
    <xsl:call-template name="span">
      <xsl:with-param name="span-text" select="substring-after( $span-text, '|')" />
      <xsl:with-param name="row" select="$row" />
      <xsl:with-param name="col" select="$col + 1" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

</xsl:stylesheet>

...会改变这个......

<Base>
  <Span>a|a| |a| |a| | |a| |a|a</Span>
  <Span>b| | |b| | |b| | | | |b</Span>
  <Span> | | | |c| | |c| | |c| </Span>
</Base>

......进入此......

<Output>
  <Period>a|b</Period>
  <Period>a</Period>
  <Period>a|b</Period>
  <Period>c</Period>
  <Period>a</Period>
  <Period>b</Period>
  <Period>c</Period>
  <Period>a</Period>
  <Period>a|c</Period>
  <Period>a|b</Period>
</Output>

答案 1 :(得分:0)

使用EXSLT时,应该执行以下操作:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:str="http://exslt.org/strings"
  xmlns:exsl="http://exslt.org/common"
  exclude-result-prefixes="str exsl"
  version="1.0">

<xsl:output indent="yes"/>

<xsl:variable name="table-rtf">
  <table>
    <xsl:apply-templates select="Base/Span" mode="tokenize"/>
  </table>
</xsl:variable>
<xsl:variable name="table" select="exsl:node-set($table-rtf)"/>

<xsl:template match="Base">
  <Output>
    <xsl:apply-templates select="$table/table/row[1]/cell" mode="col"/>
  </Output>
</xsl:template>

<xsl:template match="row/cell" mode="col">
  <xsl:variable name="pos" select="position()"/>
  <xsl:variable name="cols" select="$table/table/row/cell[$pos]"/>
  <xsl:if test="$cols[normalize-space()]">
    <Period>
      <xsl:for-each select="$cols[normalize-space()]">
        <xsl:if test="position() > 1">|</xsl:if>
        <xsl:value-of select="."/>
      </xsl:for-each>
    </Period>
  </xsl:if>
</xsl:template>

<xsl:template match="Span" mode="tokenize">
  <row>
    <xsl:for-each select="str:tokenize(., '|')">
      <cell>
        <xsl:value-of select="normalize-space()"/>
      </cell>
    </xsl:for-each>
  </row>
</xsl:template>

</xsl:stylesheet>

例如,使用xsltproc,您可以转换输入

<Base>
  <Span>a|a| |a| |a| | |a| |a|a</Span>
  <Span>b| | |b| | |b| | | | |b</Span>
  <Span> | | | |c| | |c| | |c| </Span>
</Base>

进入结果

<Output>
  <Period>a|b</Period>
  <Period>a</Period>
  <Period>a|b</Period>
  <Period>c</Period>
  <Period>a</Period>
  <Period>b</Period>
  <Period>c</Period>
  <Period>a</Period>
  <Period>a|c</Period>
  <Period>a|b</Period>
</Output>

答案 2 :(得分:0)

此模板不使用扩展名(XSLT 1.0)

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="Base">
      <Output>
          <xsl:call-template name="Format">
            <xsl:with-param name="One" select="Span[1]" />
            <xsl:with-param name="Two" select="Span[2]" />
            <xsl:with-param name="Three" select="Span[3]" />
          </xsl:call-template>
      </Output>
    </xsl:template>
  <xsl:template name="Format">
    <xsl:param name="One" />
    <xsl:param name="Two" />
    <xsl:param name="Three" />
    <xsl:param name="SOne" select="substring-before($One,'|')"/>
    <xsl:param name="STwo" select="substring-before($Two,'|')"/>
    <xsl:param name="SThree" select="substring-before($Three,'|')"/>
    <xsl:choose>
      <xsl:when test="string-length($One) &gt; 1 or string-length($Two) &gt;1 or string-length($Three) &gt; 1">
        <xsl:if test="not($SOne='' and $STwo ='' and $SThree='')">
          <Period>
            <xsl:call-template name="FormatPipes">
              <xsl:with-param name="One" select="$SOne" />
              <xsl:with-param name="Two" select="$STwo" />
              <xsl:with-param name="Three" select="$SThree" />
            </xsl:call-template>
          </Period>
        </xsl:if>
        <xsl:call-template name="Format">
          <xsl:with-param name="One" select="substring-after($One,'|')" />
          <xsl:with-param name="Two" select="substring-after($Two,'|')" />
          <xsl:with-param name="Three" select="substring-after($Three,'|')" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="not($One='' and $Two ='' and $Three='')">
          <Period>
            <xsl:call-template name="FormatPipes">
              <xsl:with-param name="One" select="$One" />
              <xsl:with-param name="Two" select="$Two" />
              <xsl:with-param name="Three" select="$Three" />
            </xsl:call-template>
          </Period>
        </xsl:if>
      </xsl:otherwise>
      </xsl:choose>
  </xsl:template>

  <xsl:template name="FormatPipes">
    <xsl:param name="One" />
    <xsl:param name="Two" />
    <xsl:param name="Three" />
    <xsl:variable name="node">
      <root>
        <xsl:if test="not($One='')">
          <a><xsl:value-of select="$One"/></a>
        </xsl:if>
        <xsl:if test="not($Two='')">
          <a><xsl:value-of select="$Two"/></a>
        </xsl:if>
        <xsl:if test="not($Three='')">
          <a><xsl:value-of select="$Three"/></a>
        </xsl:if>
      </root>
    </xsl:variable>
    <xsl:apply-templates select="msxsl:node-set($node)"/>
  </xsl:template>

  <xsl:template match="root">
    <xsl:for-each select="a">
    </xsl:for-each>
    <xsl:apply-templates select="a"/>
  </xsl:template>

  <xsl:template match="a">
    <xsl:value-of select="."/>
    <xsl:if test="position() != last()">|</xsl:if>
  </xsl:template>

</xsl:stylesheet>

将产生正确的输出

   <?xml version="1.0" encoding="utf-8"?>
<Output>
  <Period>a|b</Period>
  <Period>a</Period>
  <Period>a|b</Period>
  <Period>c</Period>
  <Period>a</Period>
  <Period>b</Period>
  <Period>c</Period>
  <Period>a</Period>
  <Period>a|c</Period>
  <Period>a|b</Period>
</Output>