CageE
CageE

Reputation: 455

how locate an element in source xml file dynamically in a loop

if we have the following xml input:

<root xmlns:ns="http://www.blabla">
  <ns:element1 attribute="attr1">10</ns:element1>
  <ns:element1 attribute="attr2">20</ns:element1>

  <ns:element2 attribute="attr1">30</ns:element1>
  <ns:element2 attribute="attr2">40</ns:element1>
</root>

how can we locate each element inside a for-each in xslt?

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="root">
        <html>
            <body>
                <h2>My Collection</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th style="text-align:left">Column 1</th>
                        <th style="text-align:left">Column 2</th>
                    </tr>
                    <xsl:for-each select="1 to 10">
                        <xsl:variable name="name" select="concat('element', .)"/>
                        <tr>
                        <td>
                            <xsl:value-of select="???what should be here???[lower-case(@attribute)='attr0']"/>
                        </td>
                        <td>
                            <xsl:value-of select="???same question???[lower-case(@attribute)='attr1']"/>
                        </td>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>

Please note that I want to follow this procedure (if possible) as the input is very dynamic and we don't always get all elements in every row. I appreciate your help.

Upvotes: 0

Views: 54

Answers (2)

Amrendra Kumar
Amrendra Kumar

Reputation: 1816

If you dont have control try to take count of the elements and run the loop as per count like this:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:ns="http://www.blabla">
    <xsl:template match="root">
        <html>
            <body>
                <h2>My Collection</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th style="text-align:left">Column 1</th>
                        <th style="text-align:left">Column 2</th>
                    </tr>
                    <xsl:variable name="all-element" select="count(//*:element)"/>
                    <xsl:for-each select="*[concat('ns:element', (1 to $all-element))]">
                        <xsl:message select="."/>
                        <tr>
                            <td>
                                <xsl:value-of select="@attribute"/>
                            </td>
                            <td>
                                <xsl:value-of select="."/>
                            </td>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Tim C
Tim C

Reputation: 70618

The expression you want is this...

<xsl:value-of select="*[local-name() = $name][lower-case(@attribute)='attr1']"/>

... Except this will fail with an error along the lines of Required item type of context item for the child axis is node(); supplied expression (.) has item type xs:integer, due to the code being executed in the context of the xsl:for-each on atomic values. To get around this, you will need to save a reference to the child elements in a variable before the xsl:for-each.

Try this XSLT

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="root">
        <html>
            <body>
                <h2>My Collection</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th style="text-align:left">Title</th>
                    </tr>
                    <xsl:variable name="children" select="*" />
                    <xsl:for-each select="1 to 10">
                        <xsl:variable name="name" select="concat('element', .)"/>
                        <tr>
                        <td>
                            <xsl:value-of select="$children[local-name() = $name][lower-case(@attribute)='attr1']"/>
                        </td>
                        <td>
                            <xsl:value-of select="$children[local-name() = $name][lower-case(@attribute)='attr2']"/>
                        </td>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

Or slightly better, to reduce code duplication...

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="root">
        <html>
            <body>
                <h2>My Collection</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th style="text-align:left">Title</th>
                    </tr>
                    <xsl:variable name="children" select="*" />
                    <xsl:for-each select="1 to 10">
                        <xsl:variable name="name" select="concat('element', .)"/>
                        <xsl:variable name="element" select="$children[local-name() = $name]"/>
                        <tr>
                        <td>
                            <xsl:value-of select="$element[lower-case(@attribute)='attr1']"/>
                        </td>
                        <td>
                            <xsl:value-of select="$element[lower-case(@attribute)='attr2']"/>
                        </td>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

Note, I would really consider changing your input XML if you have control over it. Numbering elements using the element name makes it harder to manipulate. Ideally you would do this instead...

<ns:element num="1" attribute="attr1">10</ns:element>

Upvotes: 2

Related Questions