tags:

views:

559

answers:

4

I have some XML code that looks like this

<SEARCHRESULTS>
  <FUNCTION name="BarGraph">
    <PARAMETER name="numList"></PARAMETER>
    <PARAMETER name="maxValue"></PARAMETER>
    <CODE>Some code</CODE>
  </FUNCTION>
</SEARCHRESULTS>

And I want to extract a list of parameter names for each function, so far I've got the following xsl code

<xsl:for-each select="SEARCHRESULTS/FUNCTION">
  <ROW>
    <COL><DATA><xsl:value-of select="@name" /></DATA></COL>
    <COL><DATA><xsl:value-of select="PARAMETER/@name" /></DATA></COL>
    <COL><DATA><xsl:value-of select="CODE" /></DATA></COL>
  </ROW>
</xsl:for-each>

which of course returns the name of the first parameter along with the function name and code.

I want a list of all the parameters for the function in a text string. Return separated is best, but as long as all the names are in the string I can parse it later on.

Any help would be much appreciated, happy new year.

EDIT - I could normalise the parameter records out in the target database - but I'm not going to, I just need them for display purposes really so I don't want to put too much effort in. This is why I'm looking for a simple text string.

I thought there might be some way of just putting an asterisk in or something. If not I'll create a variable and add another for-each to build a string - but it just seems like there should be a simpler way

EDIT - The resulting XML should look like

<ROW>
  <COL><DATA>BarGraph</DATA></COL>
  <COL><DATA>numList;maxValue</DATA></COL>
  <COL><DATA>Some code</DATA></COL>
</ROW>

Where the ';' in the second column could be a carriage return or another character that I can specify

+2  A: 

Try with a second foreach :

<xsl:template match="/">
<xsl:for-each select="SEARCHRESULTS/FUNCTION">
  <ROW>
    <COL>
      <DATA>
        <xsl:value-of select="@name" />
      </DATA>
    </COL>
    <COL>
      <DATA>
        <xsl:for-each select="PARAMETER">
          <!-- separate the names by a semicolon (do not insert a semicolon the first time -->
          <xsl:if test="position() > 1">
            <xsl:text>;</xsl:text>
          </xsl:if>
          <xsl:value-of select="@name" />
        </xsl:for-each>
      </DATA>
    </COL>
    <COL>
      <DATA>
        <xsl:value-of select="CODE" />
      </DATA>
    </COL>
  </ROW>
</xsl:for-each>

0xA3
I've marked this answer as accepted as it is literally the one I accepted and used. I haven't used the other xsl:apply-templates solutions because I'm trying to keep the xsl as easy to understand as possible
Matt Haughton
+1  A: 
<xsl:template match="PARAMETER">
    <xsl:value-of select="@name" />
    <xsl:text> </xsl:text>
</xsl:template>

Replace the current code

  <COL><DATA><xsl:value-of select="PARAMETER/@name" /></DATA></COL>

with

  <COL><DATA><xsl:apply-templates select="PARAMETER"/></DATA></COL>
krosenvold
Where does the <xsl:template match="PARAMETER"> code go, would it go above the initial <for-each>?
Matt Haughton
No, it should be a separate template above or below your "main" template (the one that matches '/').
0xA3
What does the <xsl:text> code do?
Matt Haughton
<xsl:text> </xsl:text> simply inserts a blank character to separate the parameter names.
0xA3
Ah, am I right to assume that this code will automatically concatenate all the parameters into a single text string then? And I could replace that blank with a carriage return for example (some of the parameters will have spaces)?
Matt Haughton
Then this is closer to what I was looking for. I'm having a few problems getting it working, not sure what's wrong yet, but thanks for all your help
Matt Haughton
I updated my answer to your desired output
0xA3
+1  A: 

One possible transformation that produces the required result is the following:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <!--                                              -->
    <xsl:template match="/*">
      <TABLE>
        <xsl:apply-templates select="*"/>
      </TABLE>
    </xsl:template>
 <!--                                              -->
    <xsl:template match="FUNCTION">
      <ROW>
        <COL><xsl:value-of select="@name"/></COL>
        <COL><xsl:apply-templates select="PARAMETER"/></COL>
        <COL><xsl:apply-templates select="CODE"/></COL>
      </ROW>
    </xsl:template>
 <!--                                              -->
<xsl:template match="PARAMETER">
 <xsl:param name="pDelim" select="';'"/>
 <xsl:value-of select=
 "concat(@name,
         substring($pDelim,
                   1 div not(position() = last()))
                   )"/>
</xsl:template>
</xsl:stylesheet>

When this transformation is applied on the original XML document:

<SEARCHRESULTS>
    <FUNCTION name="BarGraph">
     <PARAMETER name="numList"></PARAMETER>
     <PARAMETER name="maxValue"></PARAMETER>
     <CODE>Some code</CODE>
    </FUNCTION>
</SEARCHRESULTS>

The wanted result is produced:

<TABLE>
   <ROW>
      <COL>BarGraph</COL>
      <COL>numList;maxValue</COL>
      <COL>Some code</COL>
   </ROW>
</TABLE>

Do note that the <xsl:template> which matches PARAMETER has a parameter $pDelim, which may be used to specify what character (or string) to use as a delimiter for the list of parameter names. The default value of this delimiter is set to ";".

Dimitre Novatchev
+2  A: 

xsl:apply-templates is going to be your answer. It's simple and elegant.

<xsl:template match="FUNCTION/PARAMETER">
    <xsl:if test="position() > 1">
        <xsl:text>; </xsl:text> <!-- This is the delimiter -->
    </xsl:if>
    <xsl:value-of select="@name" />
</xsl:template>

Then instead of the <xsl:value-of select="PARAMETER/@name" /> you make it this:

<xsl:apply-templates select="PARAMETER" />
sirlancelot