How do I Format a list <ul> <li> using XSL - (XML into an HTML page)

Hi XSL - XML Experts
I am trying to get the list of ingredients from the Recipe1.xml file into the following format using the xsl stylesheet in the code window below. (a copy of the xml file is at the end of the xsl stylesheet code)

Desired Format:
   1 tsp yeast
   2 tsp sugar
   0.5 cup warm water
   1 cup flour

The current code in the xsl file below generates this:
              120.51tsptspcupcupyeastsugarwarm waterflour

Could someone correct or modify the xsl code below to achieve the desired list format?

Kind regards 2bears
<ul>
<li>
 
<xsl:for-each select="recipe">
<xsl:for-each select="ingredients">
<xsl:for-each select="ingredient">
<xsl:for-each select="@quantity">
<xsl:value-of select="string(.)"/>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
 
<xsl:for-each select="recipe">
<xsl:for-each select="ingredients">
<xsl:for-each select="ingredient">
<xsl:for-each select="@measure">
<xsl:value-of select="string(.)"/>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
 
<xsl:for-each select="recipe">
<xsl:for-each select="ingredients">
<xsl:for-each select="ingredient">
<xsl:apply-templates/>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
 
</li>
</ul>
<!--........................................XML FILE FOLLOWS-->
<recipe>
	<title created-date="2005-06-01" modified_date="2005-06-01">Bush Bread</title>
	<number_of_servings>Makes enough for one loaf.</number_of_servings>
	<comment>Original recipe.</comment>
	<ingredients heading="Ingredients">
		<ingredient quantity="1" measure="tsp">yeast</ingredient>
		<ingredient quantity="2" measure="tsp">sugar</ingredient>
		<ingredient quantity="0.5" measure="cup">warm water</ingredient>
		<ingredient quantity="1" measure="cup">flour</ingredient>
	</ingredients>
	<preparation_instructions id="1">
        Mix the sugar, yeast and water and leave in a warm
        place for about 10 minutes. Blaa, blaa, blaa>
	<cooking_instructions>
        Cook at 180 degC for ten minutes.</cooking_instructions>
</recipe>

Open in new window

2bearsAsked:
Who is Participating?
 
zc2Commented:
I skipped all the properties except the "title", you can add them same way.
The list of ingredients is created by single for-each loop:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
	<xsl:template match="recipe">
		<h1><xsl:value-of select="title"/></h1>
	
		<ul>
			<xsl:for-each select="ingredients/ingredient">
			<li>
				<xsl:value-of select="@quantity"/><xsl:text> </xsl:text>
				<xsl:value-of select="@measure"/><xsl:text> </xsl:text>
				<xsl:value-of select="."/>
			</li>
   			</xsl:for-each>
		</ul>
	</xsl:template>
</xsl:stylesheet>

Open in new window

0
 
abelCommented:
Can you show your desired format in XML/HTML code? I can guess, but it would be nice to know we're on the same page. Your current code is indeed incorrect, because all it does is loop the data (in a very complex way) and outputs it inside one single < li > element.
0
 
abelCommented:
Here's a valid XSLT you can use. It produces the output as in the following post. The code is commented and should be self-explanatory with the comments.



<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:output method="html" indent="yes"/>
 
    <!-- this means: throw away everything we do not
    explicitly specify, but don't stop processing -->
    <xsl:template match="* | node()" >
        <!-- process the children of current node -->
        <xsl:apply-templates />
    </xsl:template>
 
    <!-- starting point for matching, the root node -->
    <xsl:template match="/">
        <body>
            <xsl:apply-templates />
        </body>
    </xsl:template>
 
    <!-- each recipe starts with a heading -->
    <xsl:template match="recipe">
        <h3>
            <xsl:value-of select="title"/>
        </h3>
        <!-- apply child nodes -->
        <xsl:apply-templates />
    </xsl:template>
 
    <!-- list of ingredients is where the <ul> starts -->
    <xsl:template match="ingredients">
        <ul>
            <xsl:apply-templates />
        </ul>
    </xsl:template>
 
    <!-- each ingredient goes on one <li> on its own -->
    <xsl:template match="ingredient">
        <li>
            <xsl:value-of select="@quantity" />
            <xsl:value-of select="@measure" />
            <xsl:value-of select="." />
        </li>
    </xsl:template>
    
    <!-- the instructions go in another <p> -->
    <xsl:template match="preparation_instructions">
        <p>
            <!-- select first text node, just "." would not work here -->
            <xsl:value-of select="node()"/>            
        </p>
        <!-- cooking instructions also inside ul?-->
        <ul>
            <xsl:apply-templates />
        </ul>
    </xsl:template>
 
    <xsl:template match="cooking_instructions">
        <li>
            <xsl:value-of select="." />
        </li>
    </xsl:template>
 
 
</xsl:stylesheet>

Open in new window

0
Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
abelCommented:
Ah, I see that zc2 also provided a solution meanwhile. Anyway, as promised, here's the output:

<body>
  <h3>Bush Bread</h3>
  <ul>
    <li>1tspyeast</li>
    <li>2tspsugar</li>
    <li>0.5cupwarm water</li>
    <li>1cupflour</li>
  </ul>
  <p>
		Mix the sugar, yeast and water and leave in a warm
	place for about 10 minutes. Blaa, blaa, blaa
		</p>
  <ul>
    <li>
			Cook at 180 degC for ten minutes.
		</li>
  </ul>
</body>

Open in new window

ScreenShot237.png
0
 
2bearsAuthor Commented:
Hi zc2 and abel
Thank you for the prompt and comprehensive solutions.
In the end I used a snippet from zc2's answer (see below) which worked with a minor modification to the "template match" and the first "xsl:for-each select" values.
...............................
CODE:


  •  
     


.............................
Hence, I have awarded 350 points to zc2. I have also awarded 150 points to abel for the effort evident in your answer, although nearly identical, it was slightly confusing with the
  • elements located outside the
    • elements.
    • Hope this point distribution is ok - Many thanks for both your excellent efforts.
    • Kind regards 2bears
  • 0
     
    abelCommented:
    I repeat your Grading-comment here, because it contained the final solution, which is good for archive readers:

    Hi zc2 and abelThank you for the prompt and comprehensive solutions.In the end I used a snippet from zc2's answer (see below) which worked with a minor modification to the "template match" and the first "xsl:for-each select" values................................CODE:<snip>, see below</snip>.............................Hence, I have awarded 350 points to zc2. I have also awarded 150 points to abel for the effort evident in your answer, although nearly identical, it was slightly confusing with the <li> elements located outside the <ul> elements.Hope this point distribution is ok - Many thanks for both your excellent efforts.Kind regards 2bears
    Thank you too and glad it helped. With the xml you provided there were no [li] outside [ul] elements (see above, I showed the output).

    I can understand your choice of solution, because in the end you clearly only needed that information and then what you showed is just most straightforward. I just wanted to show you the "xslt way" of using matching templates, where the burden of using xsl:for-each is taken away completely and you have more flexibility in processing your XML.

    -- Abel --

    <xsl:template match="/">
      <ul>
        <xsl:for-each select="recipe/ingredients/ingredient">
          <li>
            <xsl:value-of select="@quantity"/><xsl:text> </xsl:text>
            <xsl:value-of select="@measure"/><xsl:text> </xsl:text>
            <xsl:value-of select="."/>
          </li>
        </xsl:for-each>
      </ul>
    </xsl:template>
    

    Open in new window

    0
    Question has a verified solution.

    Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

    Have a better answer? Share it in a comment.

    All Courses

    From novice to tech pro — start learning today.