Проблема вызова рекурсии в xsl с последующим братом

У меня есть следующий xml, который содержит информацию о продуктах. Описание каждого продукта иногда появляется в следующих строках перед следующим продуктом, потому что после определенного количества символов описание разбивается.

<?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>

Что творится? Проблема в том, что когда я снова звоню в buildDescription! Но я не знаю, как это исправить!

Большое спасибо!


person jechaviz    schedule 31.01.2011    source источник
comment
Вы просите нас читать много XML. Все ли это необходимо для иллюстрации проблемы?   -  person John Saunders    schedule 31.01.2011
comment
Спасибо, Джон! Я изменил способ, которым я просил! Я ожидаю, что это будет более ясно   -  person jechaviz    schedule 31.01.2011
comment
ibm.com/developerworks/xml/library/x-xslrecur - может быть полезно для вас.   -  person Geshan    schedule 13.07.2011


Ответы (1)


Это преобразование использует именованный шаблон, который при вызове с параметром 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 дочерний элемент следующей строки не является числом, а это означает, что он содержит больше описательных слов.

Наконец: все это интегрировано в код операционной системы:

<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>
person Dimitre Novatchev    schedule 31.01.2011
comment
Привет Дмитрий. Спасибо за Ваш ответ! Я просмотрел его и не понимаю, как объединить мой код с вашим, лол! Если вы захотите помочь мне снова, я буду благодарен! Большое спасибо! - person jechaviz; 31.01.2011
comment
Я также интегрировал ваш код, вы его видите? Но я еще не могу получить цель! - person jechaviz; 31.01.2011
comment
@jechaviz: я отредактировал свой ответ, а также добавил последнюю часть, в которой именованный шаблон интегрируется в ваш код и дает желаемые результаты. :) - person Dimitre Novatchev; 31.01.2011