Dino
Dino

Reputation: 561

For Loop within For Loop and If Statement

I have the following XML code (this is a snippet with what I am trying to work with).

 <products>
    <product>
        <productname>Name2</productname>   
        <productsize measurement="ml">250</productsize>
        <productsize measurement="ml">500</productsize>
        <productsize measurement="ml">750</productsize>
        <otherelements></otherelements>
        <price size="250" packsize="1">1.25</price>
        <price size="250" packsize="6">7.50</price>
        <price size="250" packsize="12">7.00</price>
        <price size="500" packsize="1">1.75</price>
        <price size="500" packsize="6">10.50</price>
        <price size="500" packsize="12">19.00</price>
        <price size="750" packsize="1">2.25</price>
        <price size="750" packsize="6">13.50</price>
        <price size="750" packsize="12">25.00</price>
    </product>
    <product>
        <productname>Name1</productname>   
        <productsize measurement="ml">250</productsize>
        <productsize measurement="ml">750</productsize>
        <otherelements></otherelements>
        <price size="250" packsize="1">1.25</price>
        <price size="250" packsize="6">7.50</price>
        <price size="250" packsize="12">7.00</price>
        <price size="750" packsize="1">2.25</price>
        <price size="750" packsize="6">13.50</price>
        <price size="750" packsize="12">25.00</price>
    </product>
 </products>

What I am trying to do is display it like this

250ml 1=£1.25, 6=£7.50, 12=£7.00
500ml 1=£1.75, 6=£10.50, 12=£19.00 
750ml 1=£2.25, 6=£13.50, 12=£25.00

But it's no working and here is my XSL Code what and I doing wrong, I know it's got to do with the inner for loop.

<xsl:for-each select="x:productsize">
<p>
   <xsl:value-of select="."/>
   <xsl:value-of select="@measurement"/>
</p>

   <xsl:for-each select="x:price">
   <xsl:if test="productsize = '@size'">
       <xsl:value-of select="."/>
   </xsl:if>
   </xsl:for-each>                                
</xsl:for-each>

Upvotes: 1

Views: 107

Answers (2)

freefaller
freefaller

Reputation: 19953

Firstly, your XML is invalid... you cannot have an element called <other elements></other elements>. And your closing <product> should be </product>. But I guess that is just written in there for the question.

The main issue is that <price> is not WITHIN <productsize>... therefore when you run <xsl:for-each> on the productsize you are searching for effectively /productsize/price

Try this instead...

<xsl:for-each select="product/productsize">
  <xsl:variable name="sizevar" select="."/>
  <p>
    <xsl:value-of select="."/>
    <xsl:value-of select="@measurement"/> &#160;
    <xsl:for-each select="../price[@size=$sizevar]">
      <xsl:value-of select="@packsize"/> =
      <xsl:value-of select="."/>, &#160;
    </xsl:for-each>                                
  </p>
</xsl:for-each>

This takes the value of the size attribute from the <productsize>, and uses it to find those <price> elements with the same size attribute

See this XMLPlayground for a working demo


As per the OP's comments, the following line was matching all <price> elements in the entire document...

<xsl:for-each select="//price[@size=$sizevar]">

So I have changed it to the following, which will only find those elements in the parent (i.e. the <productsize> element the <price> elements are children of.)

<xsl:for-each select="../price[@size=$sizevar]">

Upvotes: 4

helderdarocha
helderdarocha

Reputation: 23637

Assuming your source has a namespace, it should have a xmlns declaration either in the product element or some ancestor. I will assume something like this:

<product xmlns="your-namespace"> 
    <productsize measurement="ml">250</productsize>
    <productsize measurement="ml">500</productsize>
    ...
</product>

In that case, you also have to declare that namespace in your XSL:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:x="your-namespace" version="1.0"> ... </xsl:stylesheet>

Then, to obtain the result you want, you can use this stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:x="your-namespace"  version="1.0">

    <xsl:output method="text"/>

    <xsl:template match="/">
        <xsl:apply-templates select="x:product/x:productsize"/>
    </xsl:template>

    <xsl:template match="x:productsize">
        <xsl:variable name="size" select="."></xsl:variable>
        <xsl:value-of select="."/>
        <xsl:value-of select="@measurement"/><xsl:text> </xsl:text>
        <xsl:apply-templates select="../x:price[@size=$size]"/><xsl:text>
</xsl:text>
    </xsl:template>

    <xsl:template match="x:price">
        <xsl:value-of select="@packsize"/><xsl:text>=£</xsl:text>
        <xsl:value-of select="."/>
        <xsl:if test="not(position() = last())">
            <xsl:text>, </xsl:text>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

As for why your stylesheet didn't work, there are many possible reasons. If the namespaces are correctly declared, you are trying to read price in the context of productsize, but they are not nested. You would have to access each price moving out of your context, either using a relative or absolute XPath expression. I used ../price above, but it would also work with //price.

I am assuming that any other code you did not post and couldn't be assumed from your examples is not relevant. If you have other product elements or if products is not actually root, you will have to adapt the stylesheet.

Upvotes: 2

Related Questions