views:

43

answers:

2

I have an xml file that looks like this:

<args>
  <sometag value="abc">
  <anothertag value="def">
  <atag value="blah">
</args>

keep in mind that tag names within args could be named anything (I don't know ahead of time) Now i have this xml file stored in a variable called $data which I loaded using a document() call in the xslt stylesheet (its not the backing data for the xslt file)

I want to take that data and produce the following output: sometag=abc&anothertag=def&atag=blah

so (a very simplified verison looks like this:

<xsl:template>
 <xsl:variable name="data"  select="document('/path/to/xml')" />

  <xsl:call-template name='build_string'>
    <xsl:with-param name='data' select='$data' />

  </xsl:call-template>

</xsl:template>

<!-- here is where i need help -->
<xsl:template name="build_string">
  <xsl:param name='data'>
  <xsl:value-of select='name($data/.)' />=<xsl:value-of select='$data/@value' />

  <xsl:if test='$data/following-sibling::node()'>
    <xsl:text>&#38;</xsl:text>
    <xsl:call-template name="build_str">
     <xsl:with-param name="data" select='$nodes/following-sibling::node()' />
    </xsl:call-template>
  </xsl:if>


</xsl:template>

This almost works but it also prints text nodes from the input file and I don't want to match text nodes..

+3  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/*/*">
  <xsl:value-of select="concat(name(),'=',@value)"/>

  <xsl:if test="not(position()=last())">
    <xsl:text>&amp;</xsl:text>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<args>
  <sometag value="abc"/>
  <anothertag value="def"/>
  <atag value="blah"/>
</args>

produces the wanted, correct result:

sometag=abc&anothertag=def&atag=blah
Dimitre Novatchev
+1 Good answer.
Alejandro
How would I do this if I had data in a variable which I wanted to make into a string
Matt Wolfe
@Matt: THis is not clear -- you need to update your question and to show exactly what is contained in the `$data` variable. Depending on this unknown information the answer may be different.
Dimitre Novatchev
the $data variable holds the <args>
Matt Wolfe
@Matt: change the template match attribute to `match="args/*"`, probably add a `mode="build-string"`, and then from another template, `<xsl:apply-templates select="$args/*" mode="build-string"/>`.
LarsH
@Matt: just `<xsl:apply-templates select="$data/*"/>`
Dimitre Novatchev
Oh ok i see what you did there
Matt Wolfe
+1  A: 

I ended up realizing I could just use a for-each loop.. I'm not sure why I didnt use that to begin with. I'm still wondering how I could recursively iterate a list of adjacent nodes the way I was doing before (which wasn't working correctly because it was also catching text nodes and doing other weird things I couldn't understand). Here is my solution (I also added a separator variable)

<xsl:template name='string_builder'>
    <xsl:param name='data' />
    <xsl:param name='separator' />        
    <xsl:for-each select='$data/*'>
        <xsl:value-of select='name()'/>=<xsl:value-of select='@value'/>
        <xsl:if test='position() != last()'>
           <xsl:value-of select='$separator'/>
        </xsl:if>
    </xsl:for-each>
</xsl:template>
Matt Wolfe
@Matt, the reason your recursive implementation was catching text nodes was because you passed the data parameter as `$nodes/following-sibling::node()` instead of `$data[1]/following-sibling::*`. The `*` matches only elements, while `node()` matches text nodes and others too. Better would be `$data[position() != 1]`, if $data contains only the child elements of the args element.
LarsH