XSLT - maintaining iteration count

Discussion in 'XML' started by M.Kamermans, Jan 29, 2005.

  1. M.Kamermans

    M.Kamermans Guest

    I have an XSLT script that has to operate on some particularly nasty XML to
    turn it into a SQL databse, but 'entries' in the XML aren't actually
    unique, so their unique identifier is useless. I need to somehow create
    crosslinked tables using the XSLT, but can only do this by maintaining a
    counter for each line I write.

    Is there a way to create a proxy 'increment' function that lets me maintain
    a counter variable?

    - Mike Kamermans
     
    M.Kamermans, Jan 29, 2005
    #1
    1. Advertising

  2. M.Kamermans

    M.Kamermans Guest

    To be more specific, I have an XML document consisting of entries roughly
    like this:

    <entry>
    <seq_num>81732</seq_num>
    <k_entries>
    <k val="kval1"/>
    <k val="kval2"/>
    </k_entries>
    <r_entries>
    <r val="rval1"/>
    <r val="rval2"/>
    </r_entries>
    </entry>

    The XSLT operates using a for-each for entries, and needs to do all
    combinations of [k] and [r] values. In non-XSL format I'd like to do the
    following:

    counter = 1;
    foreach (entry in document)
    foreach (k in entry)
    foreach (r in entry)
    (create a SQL like with the values of [counter/k/r])
    (increment the counter by one)
    /foreach
    /foreach
    /foreach

    I'm not sure how to make this work in XSLT.

    - Mike
     
    M.Kamermans, Jan 29, 2005
    #2
    1. Advertising

  3. something like

    <xsl:for-each select="entry">
    <xsl:for-each select="k_entries/k">
    <xsl:value-of name="k" select="."/>
    <xsl:for-each select="../../r_entries/r">
    <xsl:variable name="n">
    <xsl:number level="any" count="k|r"/>
    </xsl:variable>
    do something with . $k and $n
    ..

    David
     
    David Carlisle, Jan 29, 2005
    #3
  4. M.Kamermans

    M.Kamermans Guest

    David,

    > <xsl:for-each select="entry">
    > <xsl:for-each select="k_entries/k">
    > <xsl:value-of name="k" select="."/>
    > <xsl:for-each select="../../r_entries/r">
    > <xsl:variable name="n">
    > <xsl:number level="any" count="k|r"/>
    > </xsl:variable>
    > do something with . $k and $n
    > ..


    Sadly, that'd only maintain the count inside an entry. this would still
    leave the matter of maintaining the count for the whole document. for
    instance, a document like

    <doc>
    <entry>
    <k_entries><k val="ka"/><k val="kb"/></k_entries>
    <r_entries><r val=ra"/></r_entries>
    </entry>
    <entry>
    <k_entries><k val="kc"/></k_entries>
    <r_entries><r val=rb"/><r val=rc"/><r val=rd"/></r_entries>
    </entry>
    <entry>
    <k_entries><k val="kd"/></k_entries>
    <r_entries><n val=re"/></r_entries>
    </entry>
    </doc>

    Would have to end up looking like the following text:

    insert into entry_table (id,k,r) values (1, ka, ra);
    insert into entry_table (id,k,r) values (2, kb, ra);
    insert into entry_table (id,k,r) values (3, kc, rb);
    insert into entry_table (id,k,r) values (4, kc, rc);
    insert into entry_table (id,k,r) values (5, kc, rd);
    insert into entry_table (id,k,r) values (6, kd, re);

    Know of any way to maintain a counter that creates these 'id' values
    correctly?

    - Mike
     
    M.Kamermans, Jan 29, 2005
    #4

  5. > Sadly, that'd only maintain the count inside an entry.


    No, if you wanted that you'd have to add from="entry" the default is the
    whole document.

    > Know of any way to maintain a counter that creates these 'id' values
    > correctly?


    As I posted.

    David
     
    David Carlisle, Jan 29, 2005
    #5
  6. I wrote

    > > Sadly, that'd only maintain the count inside an entry.

    >
    > No, if you wanted that you'd have to add from="entry" the default is the
    > whole document.


    This is true.


    > > Know of any way to maintain a counter that creates these 'id' values
    > > correctly?

    >
    > As I posted.
    >

    That is true as written, but not entirely helpful as xsl:number counts
    the input tree (in this case the sum of the number of k and r nodes)
    but you want the count in the output (essentially the product in this
    case)

    Your original post indicated that actually you just need a unique
    identifier rather than a count, that's easier in xslt, the default mode
    in the code below produces
    [81732,kval1,rval1][d0e3d0e10d0e17]
    [81732,kval1,rval2][d0e3d0e10d0e19]
    [81732,kval2,rval1][d0e3d0e12d0e17]
    [81732,kval2,rval2][d0e3d0e12d0e19]
    [81732,kval1,rval1][d0e23d0e30d0e37]
    [81732,kval1,rval2][d0e23d0e30d0e39]
    [81732,kval2,rval1][d0e23d0e32d0e37]
    [81732,kval2,rval2][d0e23d0e32d0e39]

    on

    <entries>
    <entry>
    <seq_num>81732</seq_num>
    <k_entries>
    <k val="kval1"/>
    <k val="kval2"/>
    </k_entries>
    <r_entries>
    <r val="rval1"/>
    <r val="rval2"/>
    </r_entries>
    </entry>
    <entry>
    <seq_num>81732</seq_num>
    <k_entries>
    <k val="kval1"/>
    <k val="kval2"/>
    </k_entries>
    <r_entries>
    <r val="rval1"/>
    <r val="rval2"/>
    </r_entries>
    </entry>
    </entries>


    which has a duplicated entry element.
    You'll see the first [] on each line iterates twice through the k and r
    values, but the second [] on each line is unique.

    If you do need a count you have to work (a bit) harder. as shown in mode
    b which passes the "count so far" as a parameter to the template for
    entry. so in this case you get

    [81732,kval1,rval1][1]
    [81732,kval1,rval2][2]
    [81732,kval2,rval1][3]
    [81732,kval2,rval2][4]
    [81732,kval1,rval1][5]
    [81732,kval1,rval2][6]
    [81732,kval2,rval1][7]
    [81732,kval2,rval2][8]


    David


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




    <xsl:template match="entries">
    <xsl:apply-templates select="entry"/>
    <xsl:apply-templates mode="b" select="entry[1]"/>
    </xsl:template>

    <xsl:template match="entry">
    <xsl:variable name="e" select="."/>
    <xsl:for-each select="k_entries/k">
    <xsl:variable name="k" select="."/>
    <xsl:for-each select="../../r_entries/r">
    [<xsl:value-of select="concat($e/seq_num,',',$k/@val,',',@val,'][',generate-id($e),generate-id($k),generate-id(.),']')"/>
    </xsl:for-each>
    </xsl:for-each>
    </xsl:template>

    <xsl:template match="entry" mode="b">
    <xsl:param name="c" select="0"/>
    <xsl:variable name="e" select="."/>
    <xsl:variable name="nr" select="count(r_entries/r)"/>
    <xsl:for-each select="k_entries/k">
    <xsl:variable name="k" select="."/>
    <xsl:variable name="kp" select="position()"/>
    <xsl:for-each select="../../r_entries/r">
    [<xsl:value-of select="concat($e/seq_num,',',$k/@val,',',@val,'][',$c+($kp -1)*$nr + position(),']')"/>
    </xsl:for-each>
    </xsl:for-each>
    <xsl:apply-templates select="following-sibling::entry[1]" mode="b">
    <xsl:with-param name="c" select="$c + $nr * count(k_entries/k)"/>
    </xsl:apply-templates>
    </xsl:template>

    </xsl:stylesheet>
     
    David Carlisle, Jan 29, 2005
    #6
  7. "M.Kamermans" <> wrote in message
    news:Xns95EDAA199CF9mkamermascienceuvanl@213.75.12.135...
    > To be more specific, I have an XML document consisting of entries roughly
    > like this:
    >
    > <entry>
    > <seq_num>81732</seq_num>
    > <k_entries>
    > <k val="kval1"/>
    > <k val="kval2"/>
    > </k_entries>
    > <r_entries>
    > <r val="rval1"/>
    > <r val="rval2"/>
    > </r_entries>
    > </entry>
    >
    > The XSLT operates using a for-each for entries, and needs to do all
    > combinations of [k] and [r] values. In non-XSL format I'd like to do the
    > following:
    >
    > counter = 1;
    > foreach (entry in document)
    > foreach (k in entry)
    > foreach (r in entry)
    > (create a SQL like with the values of [counter/k/r])
    > (increment the counter by one)
    > /foreach
    > /foreach
    > /foreach
    >
    > I'm not sure how to make this work in XSLT.
    >
    > - Mike



    Here's a quick XSLT 2.0 + FXSL solution:

    This transformation:

    <xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:f="http://fxsl.sf.net/"
    exclude-result-prefixes="f xs"
    >

    <xsl:import href="../f/func-standardXpathFunctions.xsl"/>
    <xsl:import href="../f/func-map.xsl"/>

    <xsl:eek:utput omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="/">
    <xsl:for-each select=
    "for $indEntry in 1 to count(/*/entry),
    $ind-r_entry in 1 to count(/*/entry[$indEntry]/r_entries/r)
    return
    f:map(f:insert-before(data(/*/entry[$indEntry]/r_entries/r[$ind-r_entry]/@val),1),
    data(/*/entry[$indEntry]/k_entries/*/@val)
    ) "
    >


    <xsl:value-of select="if(position() mod 2 = 1) then
    (position() + 1) idiv 2,
    ', ', ., ', '
    else
    (. , '
    ')
    "/>
    </xsl:for-each>
    </xsl:template>
    </xsl:stylesheet>

    when applied on this source xml document:

    <cat>
    <entry>
    <seq_num>81732</seq_num>
    <k_entries>
    <k val="kval1"/>
    <k val="kval2"/>
    </k_entries>
    <r_entries>
    <r val="rval1"/>
    <r val="rval2"/>
    </r_entries>
    </entry>
    <entry>
    <seq_num>81742</seq_num>
    <k_entries>
    <k val="kval3"/>
    <k val="kval4"/>
    </k_entries>
    <r_entries>
    <r val="rval3"/>
    <r val="rval4"/>
    </r_entries>
    </entry>
    <entry>
    <seq_num>81752</seq_num>
    <k_entries>
    <k val="kval5"/>
    <k val="kval6"/>
    </k_entries>
    <r_entries>
    <r val="rval5"/>
    <r val="rval6"/>
    </r_entries>
    </entry>
    </cat>

    produces the desired result:

    1 , kval1 , rval1
    2 , kval2 , rval1
    3 , kval1 , rval2
    4 , kval2 , rval2
    5 , kval3 , rval3
    6 , kval4 , rval3
    7 , kval3 , rval4
    8 , kval4 , rval4
    9 , kval5 , rval5
    10 , kval6 , rval5
    11 , kval5 , rval6
    12 , kval6 , rval6



    Cheers,

    Dimitre Novatchev
     
    Dimitre Novatchev, Jan 30, 2005
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Giacomo
    Replies:
    7
    Views:
    1,658
    Dimitre Novatchev
    Aug 13, 2006
  2. Replies:
    1
    Views:
    403
    kyle york
    Sep 1, 2005
  3. Replies:
    2
    Views:
    382
  4. Rudi
    Replies:
    5
    Views:
    5,031
  5. efelnavarro09
    Replies:
    2
    Views:
    945
    efelnavarro09
    Jan 26, 2011
Loading...

Share This Page