1

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>
3
  • What errors are you getting? Do you have a declared namespace (if you do, please post the full source and xslt). In what context is your for-each? (please post the FULL xsl) Commented Mar 19, 2014 at 17:28
  • It seems like there will be more than one product in the source XML; if so, does each product have a unique identifier? Commented Mar 19, 2014 at 17:37
  • Yes there are namespaces all done, as helderdarocha has shown me. Apologies but a lot is missing and The page displays but I get no data. I know it's got to do with the nesting issue which freefaller has explained. Commented Mar 19, 2014 at 19:06

2 Answers 2

4

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]">
Sign up to request clarification or add additional context in comments.

7 Comments

Aha, thanks freefaller this worked, to all here yes I do have a namespace declared where I declare xmlns:x="your-namespace" version=..... My problem was the fact that I was searching in /productsize/price and price is not WITHIN productsize.
Hi freefaller, I seem to be having a problem with this code, if I have more than one product element it doesn't work [xmlplayground.com/Jl2XzC] I create a link here and you can see that is loads all the data from each product into the prices, I suspect it has something to do with the //price[@size=$sizevar] Don't know how to save that link :(
Change it from //price to ../price which will only look for price elements in the parent. //price will look for elements in the entire structure
I fixed it myself, well not really, thanks to helderdarocha i changed //price[@size=$sizevar] to ../x:price[@smoothiesize=$sizevar] So I assume that if you use // instead of .. it somehow goes back to root level? Which one is relative or absolute XPath?
That's exactly what I suggested my comment from 5 minutes ago. Yes // will find the element anywhere in the entire document, but ../ will only look in the parent of the current element
|
2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.