user1060538
user1060538

Reputation: 25

Within a XSLT, how do I access one XPath when accessing a different XPath?

The sample presented is a simplified version of what I am trying to do.

Using XSLT, how do I compare data from nodes in different (non -child) xpaths? I want to compare each cd/artist with the dvd/director to see if they match (see samples below). There are multiple cd's and dvd's, but in this simplified example I only used one of each. I tried doing the comparison within one template, but I couldn’t access the dvd xpath. And I couldn't access the dvd xpath by using a separate template. When using a separate template, I created a 'compare' template that I called (<xsl:call-template name="compare">). By access, I mean that I couldn't print out the dvd/director's value.

My Question: How do I access one xpath when accessing a different xpath? What I want to do is compare cd artist with dvd director to see if they match. And print out these values, if they match. What I can't figure out is how to get the artist and director so I can compare them. My xslt does print the cd's artist value, but I can't print out the dvd/director's value.

My samples follows:

The samples come from W3Schools. I modified them to illustrate my question.

Sample XSLT

<xsl:for-each select="catalog/cd">
   <xsl:value-of select="@artist"/>
    <xsl:for-each select="catalog/dvd">         
    <xsl:if test="@director = @artist">
        <xsl:value-of select="@director"/>
    </xsl:if>           
   </xsl:for-each>
 </xsl:for-each>

Sample XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>   
<cd title="Unchain my heart" artist="Joe Cocker">

    <country>USA</country>
    <company>EMI</company>
    <price>8.20</price>
    <year>1987</year>
</cd>

    <dvd title="My DVD Movie" director="Joe Cocker">
    <country>USA</country>
    <company>WB</company>
    <price>20.00</price>
    <year>1999</year>
    </dvd>
</catalog>

Thanks for your help in advance.

Upvotes: 2

Views: 208

Answers (4)

Wayne
Wayne

Reputation: 60414

I think the larger issue is that you're thinking in procedural terms, which is rarely the right way to approach a problem with XSLT. A better way is to first match cd elements and then select the dvd elements whose director matches the artist. Consider this stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/*/cd">
        <xsl:value-of select="artist"/>
        <xsl:apply-templates select="../dvd[director=current()/artist]"
            mode="out"/>
    </xsl:template>
    <xsl:template match="dvd" mode="out">
        <xsl:value-of select="director"/>
    </xsl:template>
    <xsl:template match="dvd"/>
</xsl:stylesheet>

The first template applies templates to all dvd elements for which the following predicate evaluates to true: director=current()/artist

Of course, as in your example, we're just printing the name Joe Cocker twice, which isn't very useful.

Note: This approach breaks down when there is more than one cd element with the same artist. More complicated grouping strategies would address that case.

Upvotes: 1

FailedDev
FailedDev

Reputation: 26930

Well some pointers :

<xsl:for-each select="catalog/cd">

When you are doing this you are "inside" each catalog/cd element. Therefore your current() represents eacy catalog/cd element.

Your "comparison function" :

<xsl:if test="@director = @artist">

Does not work since you are trying to compare attributes director and artist. But there are not director and artist attributes, and even if they were, you would be trying to compare the attributes of catalog/dvd.

So the dvd element would have had to be like <dvd director="foo" artist="bar"/> etc..

I guess that what you are really trying to do is finding cd's and dvd's which have the same artist/director. It is not clear to me what you are trying to achieve. If you elaborate you will get a better answer.

Upvotes: 0

Daniel Haley
Daniel Haley

Reputation: 52878

You can simplify your for-each and use current()...

<xsl:for-each select="catalog/cd">
  <xsl:value-of select="artist"/>
  <xsl:for-each select="../dvd[director = current()/artist]">
      <xsl:value-of select="director"/>                   
  </xsl:for-each>
</xsl:for-each>

Upvotes: 0

Steven D. Majewski
Steven D. Majewski

Reputation: 2167

Make that:

 <xsl:for-each select="/catalog/dvd">   

Without the leading slash "/", catalog/dvd is relative to the current node, which is set by <xsl:for-each select="catalog/cd">

Upvotes: 0

Related Questions