使用follow-sibling在xsl中调用递归调用问题

时间:2011-01-31 04:08:42

标签: xml xslt iteration nodes

我有以下xml,其中包含产品信息。每个产品的描述有时会出现在下一个产品之前的nexts行中,因为在一定数量的字符之后,描述会被拆分。

<?xml version="1.0" encoding="utf-8"?>
<root>
 <row>
  <rowWords>5.00</rowWords>
  <rowWords>PRODUCTCODE1</rowWords>
  <rowWords>DESCRIPTIONWORD1</rowWords>
  <rowWords>DESCRIPTIONWORD2</rowWords>
  <rowWords>DESCRIPTIONWORD3</rowWords>
  <rowWords>DESCRIPTIONWORD4</rowWords>
  <rowWords>DESCRIPTIONWORD5</rowWords>
  <rowWords>11.28</rowWords>
  <rowWords>56.40</rowWords>
 </row>
 <row>
  <rowWords>DESCRIPTIONWORD6</rowWords>
 </row>
 <row>
  <rowWords>6.00</rowWords>
  <rowWords>PRODUCTCODE2</rowWords>
  <rowWords>DESCRIPTIONWORD1</rowWords>
  <rowWords>DESCRIPTIONWORD2</rowWords>
  <rowWords>DESCRIPTIONWORD3</rowWords>
  <rowWords>DESCRIPTIONWORD4</rowWords>
  <rowWords>DESCRIPTIONWORD5</rowWords>
  <rowWords>11.00</rowWords>
  <rowWords>66.00</rowWords>
 </row>
 <row>
  <rowWords>DESCRIPTIONWORD6</rowWords>
  <rowWords>DESCRIPTIONWORD7</rowWords>
  <rowWords>DESCRIPTIONWORD8</rowWords>
 </row>
 <row>
  <rowWords>DESCRIPTIONWORD9</rowWords>
  <rowWords>DESCRIPTIONWORD10</rowWords>
 </row>
 <row>
  <rowWords>10.00</rowWords>
  <rowWords>PRODUCTCODE3</rowWords>
  <rowWords>DESCRIPTIONWORD1</rowWords>
  <rowWords>DESCRIPTIONWORD2</rowWords>
  <rowWords>10.00</rowWords>
  <rowWords>100.00</rowWords>
 </row>
  .
  . any amount of rows containing products
  . 
</root>

描述以及产品数量的变化,所以我不知道如何将产品描述整合在一起。

这是我已经完成的xsl。

<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="something">
<xsl:output indent="yes"/>
 <xsl:template match="/root">
 <a:Invoice>
  <Products>
   <xsl:for-each select="row[position()&gt;=5 and position()&lt;=last()-5]">
   <!-- With this if, i prevent create a product node with partial description-->
    <xsl:if test="number(rowWords[1])">
     <Product>
      <xsl:attribute name="quantity"><xsl:value-of select="rowWords[1]"/></xsl:attribute>
      <xsl:attribute name="productCode"><xsl:value-of select="rowWords[2]"/></xsl:attribute>
      <!--Here is the problem (Thank you Dimitri) -->
      <xsl:attribute name="description">
        <xsl:call-template name="constructDescription">
            <xsl:with-param name="pRow" select="."/>
                <xsl:with-param name="pPosition" select="position()"/>
            </xsl:call-template>
      </xsl:attribute>
      <xsl:attribute name="unitValue"><xsl:value-of select="rowWords[last()-1]"/></xsl:attribute>
      <xsl:attribute name="amount"><xsl:value-of select="rowWords[last()]"/></xsl:attribute>
     </Product>
    </xsl:if>
   </xsl:for-each>
  </Products>
 </a:Invoice>
 </xsl:template>


 <xsl:template name="constructDescription">
  <xsl:param name="pRow"/>
  <xsl:param name="pPosition"/>
    <xsl:for-each select="$pRow/*[position() >= 3 and position() &lt;= last() -2]">
      <xsl:value-of select="concat(.,' ')"/>
    </xsl:for-each>
    <xsl:if test="$pRow/following-sibling::*[not(number(*[1])=number(*[1]))]">
        <xsl:call-template name="constructDescription">
            <xsl:with-param name="pRow" select="$pRow/following-sibling::
                *[position()=number($pPosition)+1]"/>
        </xsl:call-template>
    </xsl:if>
 </xsl:template>
</xsl:stylesheet>

这是输出

    <?xml version='1.0' ?>
<a:Invoice>
  <Products>
    <Product quantity="5.00" productCode="PRODUCTCODE1" description="DESCRIPTIONWORD1 DESCRIPTIONWORD2 DESCRIPTIONWORD3 DESCRIPTIONWORD4 DESCRIPTIONWORD5 DESCRIPTIONWORD6 DESCRIPTIONWORD7 DESCRIPTIONWORD8 DESCRIPTIONWORD9 DESCRIPTIONWORD1 DESCRIPTIONWORD2 DESCRIPTIONWORD3 DESCRIPTIONWORD4 DESCRIPTIONWORD5 DESCRIPTIONWORD6 DESCRIPTIONWORD7 DESCRIPTIONWORD8 DESCRIPTIONWORD9 " unitValue="11.28" amount="56.40"/>
    <Product quantity="6.00" productCode="PRODUCTCODE2" description="DESCRIPTIONWORD1 DESCRIPTIONWORD2 DESCRIPTIONWORD3 DESCRIPTIONWORD4 DESCRIPTIONWORD5 DESCRIPTIONWORD6 DESCRIPTIONWORD7 DESCRIPTIONWORD8 DESCRIPTIONWORD9 " unitValue="11.00" amount="66.00"/>
    <Product quantity="10.00" productCode="PRODUCTCODE3" description="DESCRIPTIONWORD1 DESCRIPTIONWORD2 " unitValue="10.00" amount="100.00"/>
  </Products>
</a:Invoice>

发生了什么事?问题是当我再次调用constructDescription时!但我不知道如何解决它!

非常感谢!

1 个答案:

答案 0 :(得分:2)

此转换使用命名模板,当使用包含产品的row参数调用时,会生成一个包含完整描述的字符串

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

 <xsl:key name="kFollowingDesc"
 match="row[not(number(*[1])=number(*[1]))]"
 use="generate-id(preceding-sibling::row[number(*[1])=number(*[1])][1])"/>


 <xsl:template match="/">
  <xsl:call-template name="constructDescription">
   <xsl:with-param name="pRow" select="/*/*[1]"/>
  </xsl:call-template>
 </xsl:template>

<xsl:template name="constructDescription">
    <xsl:param name="pRow"/>

    <xsl:for-each select=
      "$pRow/*[position() >= 3
             and
               not(position() > count($pRow/*) -2)
               ]
">
  <xsl:value-of select="concat(.,' ')"/>
</xsl:for-each>
    <xsl:for-each select="key('kFollowingDesc', generate-id($pRow))">
        <xsl:value-of select="concat(.,' ')"/>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

将此转换应用于以下XML文档(您的,但结构良好且显着截断):

<root>
    <row>
        <rowWords>6.00</rowWords>
        <rowWords>PRODUCTCODE2</rowWords>
        <rowWords>DESCRIPTIONWORD1</rowWords>
        <rowWords>DESCRIPTIONWORD2</rowWords>
        <rowWords>DESCRIPTIONWORD3</rowWords>
        <rowWords>DESCRIPTIONWORD4</rowWords>
        <rowWords>DESCRIPTIONWORD5</rowWords>
        <rowWords>DESCRIPTIONWORD6</rowWords>
        <rowWords>DESCRIPTIONWORD7</rowWords>
        <rowWords>DESCRIPTIONWORD8</rowWords>
        <rowWords>DESCRIPTIONWORD9</rowWords>
        <rowWords>11.00</rowWords>
        <rowWords>66.00</rowWords>
    </row>
    <row>
        <rowWords>DESCRIPTIONWORD10</rowWords>
        <rowWords>DESCRIPTIONWORD11</rowWords>
        <rowWords>DESCRIPTIONWORD12</rowWords>
        <rowWords>DESCRIPTIONWORD13</rowWords>
    </row>
</root>

生成了正确的描述

DESCRIPTIONWORD1 DESCRIPTIONWORD2 DESCRIPTIONWORD3 DESCRIPTIONWORD4 DESCRIPTIONWORD5 DESCRIPTIONWORD6 DESCRIPTIONWORD7 DESCRIPTIONWORD8 DESCRIPTIONWORD9 DESCRIPTIONWORD10 DESCRIPTIONWORD11 DESCRIPTIONWORD12 DESCRIPTIONWORD13 

解释:此命名模板应该是不言自明的,除了测试一些项目是否为数字,即:

number($x) = number($x)

true()是一个数字(或者可以成功地转换为数字的任何类型的值)时,这会准确评估为$x

我们正在测试:

not(number(*[1])=number(*[1]))

仅当下一行的第一个true()子项不是数字时评估为rowWords - 这意味着它包含更多描述字。

最后:所有这一切,都集成到了OP的代码中:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="something">
    <xsl:output indent="yes"/>

    <xsl:key name="kFollowingDesc"
     match="row[not(number(*[1])=number(*[1]))]"
     use="generate-id(preceding-sibling::row[number(*[1])=number(*[1])][1])"/>

    <xsl:template match="/root">
        <a:Invoice>
            <Products>
                <xsl:for-each select="row[number(*[1])=number(*[1])]">
                    <!-- With this if, i prevent create a product node with partial description-->
                    <xsl:if test="number(rowWords[1])">
                        <Product>
                            <xsl:attribute name="quantity">
                                <xsl:value-of select="rowWords[1]"/>
                            </xsl:attribute>
                            <xsl:attribute name="productCode">
                                <xsl:value-of select="rowWords[2]"/>
                            </xsl:attribute>
                            <!--Here is the problem (Thank you Dimitri) -->

                            <xsl:variable name="vDescriptions">
                              <xsl:call-template name="constructDescription">
                                    <xsl:with-param name="pRow" select="."/>
                                </xsl:call-template>
                            </xsl:variable>
                            <xsl:attribute name="description">
                              <xsl:value-of select="normalize-space($vDescriptions)"/>
                            </xsl:attribute>
                            <xsl:attribute name="unitValue">
                                <xsl:value-of select="rowWords[last()-1]"/>
                            </xsl:attribute>
                            <xsl:attribute name="amount">
                                <xsl:value-of select="rowWords[last()]"/>
                            </xsl:attribute>
                        </Product>
                    </xsl:if>
                </xsl:for-each>
            </Products>
        </a:Invoice>
    </xsl:template>

    <xsl:template name="constructDescription">
        <xsl:param name="pRow"/>

        <xsl:for-each select=
          "$pRow/*[position() >= 3
                 and
                   not(position() > count($pRow/*) -2)
                   ]
    ">
      <xsl:value-of select="concat(.,' ')"/>
    </xsl:for-each>
        <xsl:for-each select="key('kFollowingDesc', generate-id($pRow))">
            <xsl:value-of select="concat(.,' ')"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

将此转换应用于他提供的新XML文档

<root>
    <row>
        <rowWords>5.00</rowWords>
        <rowWords>PRODUCTCODE1</rowWords>
        <rowWords>DESCRIPTIONWORD1</rowWords>
        <rowWords>DESCRIPTIONWORD2</rowWords>
        <rowWords>DESCRIPTIONWORD3</rowWords>
        <rowWords>DESCRIPTIONWORD4</rowWords>
        <rowWords>DESCRIPTIONWORD5</rowWords>
        <rowWords>11.28</rowWords>
        <rowWords>56.40</rowWords>
    </row>
    <row>
        <rowWords>DESCRIPTIONWORD6</rowWords>
    </row>
    <row>
        <rowWords>6.00</rowWords>
        <rowWords>PRODUCTCODE2</rowWords>
        <rowWords>DESCRIPTIONWORD1</rowWords>
        <rowWords>DESCRIPTIONWORD2</rowWords>
        <rowWords>DESCRIPTIONWORD3</rowWords>
        <rowWords>DESCRIPTIONWORD4</rowWords>
        <rowWords>DESCRIPTIONWORD5</rowWords>
        <rowWords>11.00</rowWords>
        <rowWords>66.00</rowWords>
    </row>
    <row>
        <rowWords>DESCRIPTIONWORD6</rowWords>
        <rowWords>DESCRIPTIONWORD7</rowWords>
        <rowWords>DESCRIPTIONWORD8</rowWords>
    </row>
    <row>
        <rowWords>DESCRIPTIONWORD9</rowWords>
        <rowWords>DESCRIPTIONWORD10</rowWords>
    </row>
    <row>
        <rowWords>10.00</rowWords>
        <rowWords>PRODUCTCODE3</rowWords>
        <rowWords>DESCRIPTIONWORD1</rowWords>
        <rowWords>DESCRIPTIONWORD2</rowWords>
        <rowWords>10.00</rowWords>
        <rowWords>100.00</rowWords>
    </row>   .   . any amount of rows containing products   .  
</root>

产生了想要的正确结果

<a:Invoice xmlns:a="something">
<Products>
<Product quantity="5.00" productCode="PRODUCTCODE1" description="DESCRIPTIONWORD1 DESCRIPTIONWORD2 DESCRIPTIONWORD3 DESCRIPTIONWORD4 DESCRIPTIONWORD5 DESCRIPTIONWORD6" unitValue="11.28" amount="56.40" />
<Product quantity="6.00" productCode="PRODUCTCODE2" description="DESCRIPTIONWORD1 DESCRIPTIONWORD2 DESCRIPTIONWORD3 DESCRIPTIONWORD4 DESCRIPTIONWORD5 DESCRIPTIONWORD6 DESCRIPTIONWORD7 DESCRIPTIONWORD8 DESCRIPTIONWORD9 DESCRIPTIONWORD10" unitValue="11.00" amount="66.00" />
<Product quantity="10.00" productCode="PRODUCTCODE3" description="DESCRIPTIONWORD1 DESCRIPTIONWORD2" unitValue="10.00" amount="100.00" />
</Products>
</a:Invoice>