Xslt – xsl:with-param dynamically assigned (Correct usage of xsl:with-param without select attribute)

Viewed 90

I have the following XML

<Envelope>
    <Records>
        <Record>
            <Information>
                <Value>TEST INTERNAL VALUE</Value>
            </Information>
        </Record>
        <Record>
            <Information id="123456"/>
        </Record>
        <Information id="123456">
            <Value>TEST REFERENCE VALUE</Value>
        </Information>
    </Records>
</Envelope>

Each record element can have one or more Information element which can ether have a Value element of can have a ref attribute that contains an ID that points to a separate Information element outside of the current record element

Using a Transformation sheet, I'd like to loop through each Record element, loop through each information element and then if the information element has a id attribute, use that attribute to select the Information under Records by the same attribute and feed the resulting select to the template or just feed the currently selected information element to the template

I have this transformation sheet I have so far:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template name="EOL">
<xsl:text>
</xsl:text>
    </xsl:template>
    <xsl:template match="/">
        <xsl:for-each select="//Records">
            <xsl:call-template name="Records_Template">
                <xsl:with-param name="Records" select="."/>
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="Records_Template">
        <xsl:param name="Records"/>
        <xsl:for-each select="Record">
            <xsl:call-template name="EOL"/>
            <xsl:text>Result - using template (Both Record):</xsl:text>
            <xsl:call-template name="EOL"/>
            <xsl:for-each select="Information">
                <xsl:variable name="InformationRef" select="@id"/>
                <xsl:call-template name="Information_Template">
                    <xsl:with-param name="Information">
                        <xsl:choose>
                            <xsl:when test="$InformationRef">
                                <xsl:copy-of select="$Records/Information[@id=$InformationRef]"/>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:copy-of select="."/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:with-param>
                </xsl:call-template>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="Information_Template">
        <xsl:param name="Information"/>
        <!--This is for debugging purposes. It indicates that in each case, the content of Information parameter is the same regardless of how Information was set using with-param -->
        <xsl:text>Full Start: </xsl:text>
        <xsl:call-template name="EOL"/>
        <xsl:copy-of select="$Information"/>
        <xsl:text>Full END: </xsl:text>
        <xsl:call-template name="EOL"/>
        <xsl:text>Value: </xsl:text>
        <!--Even though $Information and $Information/Value do seem to be populated as per the debugging information above This selection statement does not seem to work -->
        <xsl:value-of select="$Information/Value"/>
        <xsl:call-template name="EOL"/>
    </xsl:template>
</xsl:stylesheet>

The output of the Transformation Sheet is as follows:

Result - using template (Both Record):
Full Start: 
<Information>
                <Value>TEST INTERNAL VALUE</Value>
            </Information>Full END: 
Value: 

Result - using template (Both Record):
Full Start: 
<Information id="123456">
            <Value>TEST REFERENCE VALUE</Value>
        </Information>Full END: 
Value: 

It should be noted that if I call the Information_Template like this:

<xsl:call-template name="Information_Template">
    <xsl:with-param name="Information" select="$Records/Information[@id=$InformationRef]"/>
</xsl:call-template>

or like this

<xsl:call-template name="Information_Template">
    <xsl:with-param name="Information" select="."/>
</xsl:call-template>

then the template is able to select the content of the Value element.

EDIT: Adding more nuance.

The output of the transformation is Text and revolves around the Record element. Each Record element will have multiple child elements that function the same way as the Information element. The output need to produce information for each record containing only information relevant to the Record So this:

<Envelope>
   <Records>
       <Record>
           <Information>
               <Value>TEST INTERNAL VALUE 1</Value>
           </Information>
           <Person>
               <name>Test Name 1</name>
           </Person>
       </Record>
       <Record>
           <Information id="123456"/>
           <Person id="67890"/>
       </Record>
       <Record>
           <Information id="654321"/>
           <Person id="09876"/>
       </Record>
       <Information id="123456">
           <Value>TEST REFERENCE VALUE 2</Value>
       </Information>
       <Information id="654321">
           <Value>TEST REFERENCE VALUE 3</Value>
       </Information>
       <Person id="67890">
           <name>Test Name 2</name>
       </Person>
       <Person id="09876">
           <name>Test Name 3</name>
       </Person>
   </Records>
</Envelope>

Would need to transform to this:

===Record====
Name: Test Name 1
Information: TEST INTERNAL VALUE 1

===Record====
Name: Test Name 2
Information: TEST REFERENCE VALUE 2

===Record====
Name: Test Name 3
Information: TEST REFERENCE VALUE 3
1 Answers

Use a key to refer to your Information or Person elements to which you can refer using their id.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output method="text"/>
  
  <xsl:key name="infoKey" match="Information" use="@id"/>
  <xsl:key name="personKey" match="Person" use="@id"/>
  
  <xsl:template match="/">
    <xsl:text>Envelope Output</xsl:text>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="Envelope/Records/Record"/>
  </xsl:template>
  
  <xsl:template match="Record">
    <xsl:text>&#10;</xsl:text>
    <xsl:text>===Record===</xsl:text>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="Person"/>
    <xsl:apply-templates select="Information"/>
  </xsl:template>
  
  <xsl:template match="Information[Value]">
    <xsl:text>Information: </xsl:text><xsl:value-of select="Value"/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
  
  <xsl:template match="Information[not(Value)]">
    <xsl:text>Information: </xsl:text><xsl:value-of select="key('infoKey',@id)/Value"/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
  
  <xsl:template match="Person[name]">
    <xsl:text>Name: </xsl:text><xsl:value-of select="name"/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
  
  <xsl:template match="Person[not(name)]">
    <xsl:text>Information: </xsl:text><xsl:value-of select="key('personKey',@id)/name"/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
  
</xsl:stylesheet>

See it working here : https://xsltfiddle.liberty-development.net/6q1SDkc/1

Updated answer. Combining the Person and Information templates. I think it's clearer with them separate and not harder to maintain, but if you like this better :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output method="text"/>
  
  <xsl:key name="infoKey" match="Information" use="@id"/>
  <xsl:key name="personKey" match="Person" use="@id"/>
  
  <xsl:template match="/">
    <xsl:text>Envelope Output</xsl:text>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="Envelope/Records/Record"/>
  </xsl:template>
  
  <xsl:template match="Record">
    <xsl:text>&#10;</xsl:text>
    <xsl:text>===Record===</xsl:text>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select="Person"/>
    <xsl:apply-templates select="Information"/>
  </xsl:template>
  
  <xsl:template match="Information">
    <xsl:text>Information: </xsl:text><xsl:value-of select="concat(Value,key('infoKey',@id)/Value)"/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
  
  <xsl:template match="Person">
    <xsl:text>Name: </xsl:text><xsl:value-of select="concat(name,key('personKey',@id)/name)"/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>
  
</xsl:stylesheet>

See it working here : https://xsltfiddle.liberty-development.net/6q1SDkc/3

Related