3

I'm trying to iterate through an xml document using xsl:foreach but I need the select=" " to be dynamic so I'm using a variable as the source. Here's what I've tried:

...

<xsl:template name="SetDataPath">
  <xsl:param name="Type" />

  <xsl:variable name="Path_1">/Rating/Path1/*</xsl:variable>
  <xsl:variable name="Path_2">/Rating/Path2/*</xsl:variable>

  <xsl:if test="$Type='1'">
    <xsl:value-of select="$Path_1"/>
  </xsl:if>

  <xsl:if test="$Type='2'">
    <xsl:value-of select="$Path_2"/>
  </xsl:if>
<xsl:template>

...

    <!-- Set Data Path according to Type -->
  <xsl:variable name="DataPath">
    <xsl:call-template name="SetDataPath">
      <xsl:with-param name="Type" select="/Rating/Type" />
    </xsl:call-template> 
  </xsl:variable>

...

<xsl:for-each select="$DataPath">

...

The foreach threw an error stating: "XslTransformException - To use a result tree fragment in a path expression, first convert it to a node-set using the msxsl:node-set() function."

When I use the msxsl:node-set() function though, my results are blank.

I'm aware that I'm setting $DataPath to a string, but shouldn't the node-set() function be creating a node set from it? Am I missing something? When I don't use a variable:

<xsl:for-each select="/Rating/Path1/*">

I get the proper results.

Here's the XML data file I'm using:

<Rating>
    <Type>1</Type>
    <Path1>
       <sarah>
          <dob>1-3-86</dob>
          <user>Sarah</user>
       </sarah>
       <joe>
          <dob>11-12-85</dob>
          <user>Joe</user>
       </joe>
    </Path1>
    <Path2>
       <jeff>
          <dob>11-3-84</dob>
          <user>Jeff</user>
       </jeff>
       <shawn>
          <dob>3-5-81</dob>
          <user>Shawn</user>
       </shawn>
    </Path2>
</Rating>

My question is simple, how do you run a foreach on 2 different paths?

1
  • Difficult to say without seeing at least an XML fragment. Commented Mar 15, 2010 at 17:15

3 Answers 3

3

Try this:

   <xsl:for-each select="/Rating[Type='1']/Path1/*
                         |
                          /Rating[Type='2']/Path2/*">
Sign up to request clarification or add additional context in comments.

1 Comment

This the EXACT solution I was looking for, thank you so much! Elegant and effective =D
3

Standard XSLT 1.0 does not support dynamic evaluation of xpaths. However, you can achieve your desired result by restructuring your solution to invoke a named template, passing the node set you want to process as a parameter:

<xsl:variable name="Type" select="/Rating/Type"/>
<xsl:choose>
    <xsl:when test="$Type='1'">
        <xsl:call-template name="DoStuff">
            <xsl:with-param name="Input" select="/Rating/Path1/*"/>
        </xsl:call-template>
    </xsl:when>
    <xsl:when test="$Type='2'">
        <xsl:call-template name="DoStuff">
            <xsl:with-param name="Input" select="/Rating/Path2/*"/>
        </xsl:call-template>
    </xsl:when>
</xsl:choose>

...

<xsl:template name="DoStuff">
    <xsl:param name="Input"/>
    <xsl:for-each select="$Input">
        <!-- Do stuff with input -->
    </xsl:for-each>
</xsl:template>

Comments

0

The node-set() function you mention can convert result tree fragments into node-sets, that's correct. But: Your XSLT does not produce a result tree fragment.

Your template SetDataPath produces a string, which is then stored into your variable $DataPath. When you do <xsl:for-each select="$DataPath">, the XSLT processor chokes on the fact that DataPath does not contain a node-set, but a string.

Your entire stylesheet seems to be revolve around the idea of dynamically selecting/evaluating XPath expressions. Drop that thought, it is neither possible nor necessary.

Show your XML input and specify the transformation your want to do and I can try to show you a way to do it.

17 Comments

Thanks for your help. I added the XML data I'm using. So how would you use 2 different paths in the same foreach statement?
@Nefariousity: No, your question is not "How do you run a foreach on 2 different paths?", because this is the solution you are trying to implement. State your problem, not the solution that you think of to solve it.
By using the correct XPath. For example: If you are interested in Type 1 DOB's, you could do <xsl:for-each select="/Rating[@Type='1']/*/*/dob" />. I still think you did not fully describe what you want, exactly.
No, not like that. What about <xsl:value-of select="/Rating/*[name() = $element_name)]/*">? ($element_name could be a string you set earlier) - You still have not clearly stated what you want to do. :-) I have a feeling that a) your XML sample does not exactly reflect your situation and that b) a "desired output XML" sample is needed to find the best answer to your question.
Yes, in this case it is possible as @Aohci showed in his solution. In general, XSLT 1.0 provides the <xsl:choose> instruction for conditional actions. In XPath 2.0 (XSLT 2.0) there is an "if" (then else) expression and the code can be written much more ellegantly.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.