?
Solved

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

Posted on 2009-04-24
6
Medium Priority
?
1,056 Views
Last Modified: 2013-11-18
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

0
Comment
Question by:2bears
  • 4
6 Comments
 
LVL 39

Expert Comment

by:abel
ID: 24230232
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
 
LVL 19

Accepted Solution

by:
zc2 earned 1400 total points
ID: 24230244
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
 
LVL 39

Assisted Solution

by:abel
abel earned 600 total points
ID: 24230255
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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 39

Expert Comment

by:abel
ID: 24230261
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
 

Author Closing Comment

by:2bears
ID: 31574456
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
     
    LVL 39

    Expert Comment

    by:abel
    ID: 24231241
    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

    Featured Post

    VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

    Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

    Question has a verified solution.

    If you are experiencing a similar issue, please ask a related question

    Preface This is the third article about the EE Collaborative Login Project. A Better Website Login System (http://www.experts-exchange.com/A_2902.html) introduces the Login System and shows how to implement a login page. The EE Collaborative Logi…
    SASS allows you to treat your CSS code in a more OOP way. Let's have a look on how you can structure your code in order for it to be easily maintained and reused.
    Viewers will learn about basic arrays, how to declare them, and how to use them. Introduction and definition: Declare an array and cover the syntax of declaring them: Initialize every index in the created array: Example/Features of a basic arr…
    Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
    Suggested Courses

    864 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question