tags:

views:

6839

answers:

5

given the following xml:

<current>
  <login_name>jd</login_name>
</current>
<people>
  <person>
    <first>John</first>
    <last>Doe</last>
    <login_name>jd</login_name>
  </preson>
  <person>
    <first>Pierre</first>
    <last>Spring</last>
    <login_name>ps</login_name>
  </preson>
</people>

how can i get "John Doe" from within the current/login matcher?

i tried the following:

<xsl:template match="current/login_name">
  <xsl:value-of select="../people/first[login_name = .]"/>
  <xsl:text> </xsl:text>
  <xsl:value-of select="../people/last[login_name = .]"/>
</xsl:template>
A: 

I think what he actually wanted was the replacement in the match for the "current" node, not a match in the person node:

<xsl:variable name="login" select="//current/login_name/text()"/>

<xsl:template match="current/login_name">
<xsl:value-of select='concat(../../people/person[login_name=$login]/first," ", ../../people/person[login_name=$login]/last)'/>

</xsl:template>
Kendall Helmstetter Gelner
+3  A: 

You want current() function

<xsl:template match="current/login_name">
  <xsl:value-of select="../../people/person[login_name = current()]/first"/>
  <xsl:text> </xsl:text>
  <xsl:value-of select="../../people/person[login_name = current()]/last"/>
</xsl:template>

or a bit more cleaner:

<xsl:template match="current/login_name">
  <xsl:for-each select="../../people/person[login_name = current()]">
    <xsl:value-of select="first"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="last"/>
  </xsl:for-each>
</xsl:template>
jelovirt
thx for the current tip ... even if i really dislike the usage of for-each in xslt ...
Pierre Spring
A: 

Just to add my thoughts to the stack

<xsl:template match="login_name[parent::current]">
 <xsl:variable name="login" select="text()"/>
 <xsl:value-of select='concat(ancestor::people/child::person[login_name=$login]/child::first/text()," ",ancestor::people/child::person[login_name=$login]/child::last/text())'/>
</xsl:template>

I always prefer to use the axes explicitly in my XPath, more verbose but clearer IMHO.

Depending on how the rest of the XML documents looks (assuming this is just a fragment) you might need to constrain the reference to "ancestor::people" for example using "ancestor::people[1]" to constrain to the first people ancestor.

Matt Large
+8  A: 

I'd define a key to index the people:

<xsl:key name="people" match="person" use="login_name" />

Using a key here simply keeps the code clean, but you might also find it helpful for efficiency if you're often having to retrieve the <person> elements based on their <login_name> child.

I'd have a template that returned the formatted name of a given <person>:

<xsl:template match="person" mode="name">
  <xsl:value-of select="concat(first, ' ', last)" />
</xsl:template>

And then I'd do:

<xsl:template match="current/login_name">
  <xsl:apply-templates select="key('people', .)" mode="name" />
</xsl:template>
JeniT
Okay, ignore my answer further down, this one is great. Clean and made up of nice reusable parts.
Matt Large
i love it! thanks ... never heard of the xsl:key before ...
Pierre Spring
+1  A: 

If you need to access multiple users, then JeniT's <xsl:key /> approach is ideal.

Here is my alternative take on it:

<xsl:template match="current/login_name">
    <xsl:variable name="person" select="//people/person[login_name = .]" />
    <xsl:value-of select="concat($person/first, ' ', $person/last)" />
</xsl:template>

We assign the selected <person> node to a variable, then we use the concat() function to output the first/last names.

There is also an error in your example XML. The <person> node incorrectly ends with </preson> (typo)

A better solution could be given if we knew the overall structure of the XML document (with root nodes, etc.)

leekelleher
thanks for the tip ... works great ... i don't know why, i just dislike using variables in xslt ...
Pierre Spring