Using a variable as part of a XPath selection
Asked Answered
E

2

1

I'm looking to use a variable as part of an XPath expression.

My problem might be the msxsl node-set function... not sure. But don't let that cloud your judgement... read on...

I'm using a .NET function to load up the content file, which is passed in via bespoke XML content. The @file results in an XML file.

The bespoke XML the sits on the page looks like :

<control name="import" file="information.xml" node="r:container/r:group[@id='set01']/r:item[1]" />

The XSL looks like :

<xsl:variable name="document">
    <xsl:copy-of select="ext:getIncludedContent(@file)" />
</xsl:variable>

I'm then translating this to a node-set so I can query the document

<xsl:variable name="preset-xml" select="msxsl:node-set($document)" />   

The source file I am loading in looks like :

<container>
    <group id="set01">
        <item>value01</item>
        <item>value02</item>
        <item>value03</item>
    </group>
    <group id="set02">
        <item>value04</item>
        <item>value05</item>
    </group>                    
</container>

It works up until this point. I can see the source file being brought thru and output as XML.

My problem comes in when I am trying to query the source file with an XPath expression fed in from the content.

I've tried :

<xsl:value-of select="$preset-xml/@node" />

Clearly that doesn't work as it looks for @node as a direct child of the loaded in XML.

I've tried :

<xsl:variable name="node" select="@node" />
<xsl:value-of select="$preset-xml/$node" />

But it doesn't like the second variable.

I've tried concat($preset-xml,'/',$node), but that still draws out the entire document in the result.

The only way I can get this working at the moment is to write out the full expression in the template :

<xsl:value-of select="$preset-xml/r:container/r:group[@id='set01']/r:item[1]" />

Which correctly brings thru value01

But that is then a hard coded solution. I want to develop a solution which can be manipulated from the content to suit any sort of imported content, with the parameters declared in the content file.

p.s. I'm using XSLT 1.0 because the tech admin won't change the Microsoft parser to support later versions of XSL, so any solution would need to be written with that in mind.

Eddo answered 13/1, 2012 at 16:40 Comment(4)
$preset-xml/@node is not looking for a child node; it's looking for an attribute.Pagandom
#1552026Thereon
Yeah - sorry. Slight typoe. It's looking for an attribute of the loaded in file. But either way, that method still wouldn't work. Still a bit stumped!Eddo
in case you do not wish to use extension functions you may split your transformation into 2 steps: first you generate a dynamic stylesheet on-the-fly basically turning the xpath expressions from @node atributes into xsl templates. next you apply the generated stylesheet to your actual content. of course, i can't say whether or not your environment admits for this kind of workflow. regardsEpiscopalism
C
0

There is no feature similar to reflection in XSLT. MS XSLT engine, in particular, compiles XPath queries when the XSLT was loaded, not when it was applied to your XML document. There is a reason to this: it separates the code (XSLT) from the data (input XML).

What are you trying to achieve by mixing up code and data; are you sure you really need to execute an arbitrary XPath expression from the input XML? Think, for example, what if the user will pass in the @name equal to e.g. //*, and then you'll send to the output the content of the entire input XML document, which might contain some sensitive data.

If you really need to do the thing you described, you may write your own XSLT extension object, which will accept the document root and the XPath query and will return the latter applied to the former. Then you'll be able to call it like that:

<xsl:value-of select="myExtensionNs:apply-xpath($preset-xml, @node)"/>

As for your original attempt with concat($preset-xml,'/',$node), concat is the string concatenating function. You supply some arguments to it, and it returns the concatenation of its string representations. So, concat($preset-xml,'/',$node) will return the string value of $preset-xml, concatenated with /, concatenated with the string value of $node. If you were trying to write a needed XPath query, concat('$preset-xml','/',$node) would seem more logical (note the quotes); still, there is no way to evaluate the resulted string (e.g. $preset-xml/r:container/r:group[@id='set01']/r:item[1]) to the value of corresponging XPath query; it could only be sent to output.

Crucible answered 15/2, 2012 at 4:43 Comment(0)
S
0

One possible trick is to call an parametrized xsl:template which carries out the crucial select comparison part and evaluate it's output as string.

I had a similar problem (count all nodes of an arbitrary attribute value) which I solved this way (btw: $sd = 'document($filename)' so I load a secondary xml files content):

<xsl:template match="/">
  <xsl:variable name="string_cntsuccess">
    <xsl:for-each select="./Testcase">
      <xsl:variable name="tcname" select="@location"/>
      <xsl:for-each select="$sd//TestCase[@state='Passed']">
        <xsl:call-template name="producedots">
          <xsl:with-param name="name" select="$tcname"/>
        </xsl:call-template>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="cntsuccess" select="string-length($string_cntsuccess)"/>
</xsl:template>

<xsl:template name="producedots">
  <xsl:param name="name"/>
  <xsl:variable name="ename" select="@name"/>
  <xsl:if test="$name = $ename">
    <xsl:text>.</xsl:text>
  </xsl:if>
</xsl:template>
Stefanistefania answered 3/6, 2020 at 8:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.