Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested for-each loops, accessing outer element with variable from the inner loop

I'm trying to write an XSL that will output a certain subset of fields from the source XML. This subset will be determined at transformation time, by using an external XML configuration document containing the field names, and other specific information (such as the padding length).

So, this is two for-each loops:

  • The outer one iterates over the records to access their fields record by record.
  • The inner one iterates over the configuration XML document to grab the configured fields from the current record.

I've seen in In XSLT how do I access elements from the outer loop from within nested loops? that the current element in the outside loop can be stored in an xsl:variable. But then I've got to define a new variable inside the inner loop to get the field name. Which yields to the question: Is it possible to access a path in which there are two variables ?

For instance, the source XML document looks like:

<data>
    <dataset>
        <record>
            <field1>value1</field1>
            ...
            <fieldN>valueN</fieldN>
        </record>
    </dataset>
    <dataset>
        <record>
            <field1>value1</field1>
            ...
            <fieldN>valueN</fieldN>
        </record>
    </dataset>
</data>

I'd like to have an external XML file looking like:

<configuration>
    <outputField order="1">
        <fieldName>field1</fieldName>
        <fieldPadding>25</fieldPadding>
    </outputField>
    ...
    <outputField order="N">
        <fieldName>fieldN</fieldName>
        <fieldPadding>10</fieldPadding>
    </outputField>
</configuration>

The XSL I've got so far:

<xsl:variable name="config" select="document('./configuration.xml')"/>
<xsl:for-each select="data/dataset/record">
    <!-- Store the current record in a variable -->
    <xsl:variable name="rec" select="."/>
    <xsl:for-each select="$config/configuration/outputField">
        <xsl:variable name="field" select="fieldName"/>
        <xsl:variable name="padding" select="fieldPadding"/>

        <!-- Here's trouble -->
        <xsl:variable name="value" select="$rec/$field"/>

        <xsl:call-template name="append-pad">
            <xsl:with-param name="padChar" select="$padChar"/>
            <xsl:with-param name="padVar" select="$value"/>
            <xsl:with-param name="length" select="$padding"/>
        </xsl:call-template>

    </xsl:for-each>
    <xsl:value-of select="$newline"/>
</xsl:for-each>

I'm quite new to XSL, so this might well be a ridiculous question, and the approach can also be plain wrong (i.e. repeatig inner loop for a task that could be done once at the beggining). I'd appreciate any tips on how to select the field value from the outer loop element and, of course, open to better ways to approach this task.

like image 423
Xavi López Avatar asked May 03 '12 17:05

Xavi López


People also ask

What does the inner loop of the nested for loop represent?

Nested Loop to Print Pattern In this program, the outer loop is the number of rows print. Next, the inner loop is the total number of columns in each row.

Can nested for loops use the same variable?

In Python variables have function-wide scope which means that if two variables have the same name in the same scope, they are in fact one variable. Consequently, nested loops in which the target variables have the same name in fact share a single variable.

Can for each loops be nested?

for-each loop can be nested like normal for loop.


1 Answers

Your stylesheet looks almost fine. Just the expression $rec/$field doesn't make sense because you can't combine two node sets/sequences this way. Instead, you should compare the names of the elements using the name() function. If I understood your problem correctly, something like this should work:

<xsl:variable name="config" select="document('./configuration.xml')"/>
<xsl:for-each select="data/dataset/record">
    <xsl:variable name="rec" select="."/>
    <xsl:for-each select="$config/configuration/outputField">
        <xsl:variable name="field" select="fieldName"/>
        ...
        <xsl:variable name="value" select="$rec/*[name(.)=$field]"/>
        ...    
    </xsl:for-each>
    <xsl:value-of select="$newline"/>
</xsl:for-each>

Variable field is not required in this example. You can also use function current() to access the current context node of the inner loop:

<xsl:variable name="value" select="$rec/*[name(.)=current()/fieldName]"/>
like image 125
Martin Avatar answered Sep 19 '22 16:09

Martin