tags:

views:

669

answers:

3

I have a simple xml file that is a list of attributes (Name/Value pairs):

<?xml version="1.0" encoding="ISO-8859-1"?>
<Attrs>
   <Attr N="IsValid" V="true" />
   <Attr N="ID" V="99099" />
 </Attrs>

I want to create an XSLT file that outputs the values but I cant seem to get it to return the Value for the attribute


Here is my xslt:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:template match="/Attrs">
    <xsl:if test="Attr[@N='IsValid']">
      --found IsValid        
      IsValid 1: <xsl:value-of select="current()/Attr[@V]"/> 
      IsValid 2: <xsl:value-of select="Attr[@V]"/> 
    </xsl:if>
    <xsl:if test="Attr[@N='ID']">
      --found ID
      ID 1: <xsl:value-of select="current()/Attr[@V]"/> 
      ID 2: <xsl:value-of select="Attr[@V]"/> 
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>


I simply cannot get the value of the 'V' attribute after finding the appropriate attribute Name (N='name'). I do not know how to select the @V value.

Here is my output:

<?xml version="1.0" encoding="utf-8"?>
--found IsValid        
IsValid 1:  
IsValid 2: 
--found ID
ID 1:  
ID 2:

I used Anthony's solution below for the most part. I did change the template match to just use Attr and then used a choose stement to filter on my Names. In most cases I just need the Value. In other case I needed to customize, thisreally cut down on the number of templates I needed. (Thanks again for the start in the right direction to everyone who helped)

<xsl:template match="Attr">
<xsl:choose>
   <xsl:when test="@N='IsValid'">
      <xsl:value-of select="@V" />
   </xsl:when>
   <xsl:otherwise>
      <xsl:value-of select="@V" />
    </xsl:otherwise>
</xsl:choose>
</xsl:template>
+3  A: 

You should use these paths

 current()/Attr/@V

and

Attr/@V

The purpose of [ ] in Xpaths is describe a predicate, that is a test that must be true element against which it is placed if the XPath to progress.

Edit

Try this:-

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:template match="/Attrs">
    <xsl:for-each match="Attr">
      <xsl:if test="@N='IsValid'">
       --found IsValid        
       IsValid 1: <xsl:value-of select="current()/@V"/> 
       IsValid 2: <xsl:value-of select="@V"/> 
      </xsl:if>
      <xsl:if test="@N='ID'">
        --found ID
        ID 1: <xsl:value-of select="current()/@V"/> 
        ID 2: <xsl:value-of select="@V"/> 
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Note using current() outside of a predicate doesn't make a great deal of sense as you see from the output the paths using current() result in the same as those without.

Another version more in keeping with XSLT's goals (I'm dropping the current() also) is:-

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:template match="/Attrs">
    <xsl:apply-templates />
  </xsl:template>  

  <xsl:template match="Attr[@N='IsValid']">
   --found IsValid        
   IsValid: <xsl:value-of select="@V" /> 
  </xsl:template>  

  <xsl:template match="Attr[@N='ID']">
    --found ID
    ID: <xsl:value-of select="@V"/> 
  </xsl:template>
</xsl:stylesheet>
AnthonyWJones
Thanks. This is getting closer but I'm not actually on the current node. Notice my ID values in the output contain the value 'true' instead of '99099'So my out put now looks like:<?xml version="1.0" encoding="utf-8"?>--found IsValid IsValid 1: true IsValid 2: true--found IDID 1: true ID 2: true
MikeG
There's an error in the xsl, it does a for-each over attr, but the nodes are called Attr (capital).
Colin
I wanted to thank you for your help. You gave me just what I needed. Very much appreciated.
MikeG
+1  A: 

The foreach is slow, split out the templates like so:

Edit:you beat me to it..

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:template match="/Attrs">
    <xsl:apply-templates select="Attr" />  
  </xsl:template>
  <xsl:template match="Attr">
      <xsl:if test="@N='IsValid'">
       --found IsValid
        IsValid 2: <xsl:value-of select="@V"/>
       </xsl:if>
      <xsl:if test="@N='ID'">
        --found ID        
         ID 2: <xsl:value-of select="@V"/>
       </xsl:if>
  </xsl:template>
</xsl:stylesheet>
Colin
@Colin: I'm intrigued by the assertion "foreach is slow", can you provide a reference for that?
AnthonyWJones
+1  A: 

First off, unless it matters what order the attributes appear in, you should represent them in XML as, well, attributes, e.g.:

<element IsValid='true' ID='2'/>

Name/value pairs are what XML attributes are for. (As long as the values aren't crazily long strings or object hierarchies.)

That said, here's a simple set of templates that does what you're looking for:

<xsl:template match="Attrs">
   <output>
      <xsl:apply-templates select="Attr"/>
   </output>
</xsl:template>       

<xsl:template match="Attr[@N='IsValid']">
   <xsl:text>IsValid=</xsl:text>
   <xsl:value-of select="@V"/>
</xsl:template>

<xsl:template match="Attr[@N='ID']">
   <xsl:text>ID=</xsl:text>
   <xsl:value-of select="@V"/>
</xsl:template>
Robert Rossney