Tomasz Przemski
Tomasz Przemski

Reputation: 1127

How to get the current and global position of a node inside a for-each loop?

I have input xml file about structure:

<TabularXml>
  <Sheet>
    <Row id="2">
      <Cell id="1" name="A" type="String">aaa</Cell>
      <Cell id="2" name="B" type="String">1</Cell>
    </Row>
    <Row id="3">
      <Cell id="1" name="A" type="String">aaa</Cell>
      <Cell id="2" name="B" type="String">2</Cell>
    </Row>
    <Row id="3">
      <Cell id="1" name="A" type="String">bbb</Cell>
      <Cell id="2" name="B" type="String">3</Cell>
    </Row>
    <Row id="4">
      <Cell id="1" name="A" type="String">ccc</Cell>
      <Cell id="2" name="B" type="String">4</Cell>
    </Row>  
  </Sheet>
</TabularXml>

And I want output:

<Beholdninger>
   <Unique>3</Unique>
   <AllePoster>
      <A>aaa</A>
      <p><B>1<B/></p>
      <p><B>2<B/></p>
      <A>bbb</A>
      <p><B>3<B/></p>
      <A>ccc</A>
      <p><B>4<B/></p>
   </AllePoster>
</Beholdninger>

My current xslt code:

  </msxsl:script>
    <xsl:key name="product" match="//Row/Cell[@name = 'A']/text()" use="." />
    <xsl:template match="Sheet">
        <Beholdninger>                          

            <xsl:for-each select="//Row/Cell[@name = 'A']/text()[generate-id() = generate-id(key('product',.)[1])]">                
                <xsl:if test="position()=last()">
                    <Unique><xsl:value-of select="last()"/></Unique>
                </xsl:if>
            </xsl:for-each>

            <AllePoster>

            <xsl:for-each select="//Row/Cell[@name = 'A']/text()[generate-id() = generate-id(key('product',.)[1])]">

                <A><xsl:value-of select="."/></A>                                   
                <xsl:for-each select="key('product',.)">

                    <xsl:variable select="position()" name='x'/>    
                    <p><B><xsl:value-of select="//Row[@id = $x]/Cell[@name = 'B']"/></B></p>                                                        

                </xsl:for-each>                 
            </xsl:for-each>

            </AllePoster>

        </Beholdninger>
</xsl:template>
</xsl:stylesheet>

Where I getting:

<Beholdninger>
   <Unique>3</Unique>
   <AllePoster>
      <A>aaa</A>
      <p><B>1<B/></p>
      <p><B>2<B/></p>
      <A>bbb</A>
      <p><B>1<B/></p>
      <A>ccc</A>
      <p><B>1<B/></p>
   </AllePoster>
</Beholdninger>

This is because the position() function generates indexes from the beginning of each unique <A>, not based on indexes from the entire input file. How can this be solved? Or maybe I need to rewrite the script from the beginning and approach it differently?

Upvotes: 0

Views: 115

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167581

I would simplify the key to match on Row and then write templates to transform the Cells:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="a-group" match="Row" use="Cell[@name = 'A']"/>

  <xsl:template match="Sheet">
     <Beholdninger>
         <xsl:variable name="group-heads" select="Row[generate-id() = generate-id(key('a-group', Cell[@name = 'A'])[1])]"/>
         <Unique>
             <xsl:value-of select="count($group-heads)"/>
         </Unique>
         <AllePoster>
             <xsl:apply-templates select="$group-heads"/>
         </AllePoster>
     </Beholdninger> 
  </xsl:template>

  <xsl:template match="Row">
      <xsl:apply-templates select="Cell[@name = 'A'] | key('a-group', Cell[@name = 'A'])/Cell[@name = 'B']"/>
  </xsl:template>

  <xsl:template match="Cell[@name = 'A']">
      <A>
          <xsl:value-of select="."/>
      </A>
  </xsl:template>

  <xsl:template match="Cell[@name = 'B']">
      <p>
          <B>
              <xsl:value-of select="."/>
          </B>
       </p>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/ejivJsd/1

Upvotes: 1

Related Questions