Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Help on XSL 1

Status
Not open for further replies.

zaichik

MIS
Aug 7, 2003
29
US
Hi,
I am trying to transform some XML data using XSL. Essentially I have a bunch of invoices encoded in XML and I want to display them all in a browser. I need to convert an XSL sheet that is geared to display one invoice (with a slightly different data set) to display many invoices.

Here is an abbreviated version of the XML:

<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet href="invoice.xsl" type="text/xsl"?>
<database URL="jdbc:eek:dbc:flamingo">
<invoices QUERY="SELECT * FROM invoice WHERE shipToState = 'NSW'">
<invoices_rec>
<invoiceNum>102</invoiceNum>
<salesDate>30. Jul. 01</salesDate>
<shipToAddress>11 Hollsworth St</shipToAddress>
<shipToCity>Paramatta</shipToCity>
<shipToState>NSW</shipToState>
<shipToZip>2421</shipToZip>
<contactName>Sally</contactName>
<contactPhone>6789-4321</contactPhone>
</invoices_rec>
</invoices>
<lines QUERY="SELECT i.invoiceNum, l.prodCode, l.salesQty, p.prodName, p.prodDesc,
p.prodPrice, l.salesQty * p.prodPrice AS extendedPrice FROM invoice i INNER JOIN
(lines l INNER JOIN products p ON l.prodCode = p.prodCode) ON
i.invoiceNum = l.invoiceNum WHERE i.shipToState = 'NSW'">
<lines_rec>
<invoiceNum>102</invoiceNum>
<prodCode>FP002</prodCode>
<salesQty>20</salesQty>
<prodName>Flamingo Towels</prodName>
<prodDesc>Terry towling material printed with flamingos</prodDesc>
<prodPrice>$15.00</prodPrice>
<extendedPrice>$300.00</extendedPrice>
</lines_rec>
<lines_rec>
<invoiceNum>103</invoiceNum>
<prodCode>FP002</prodCode>
<salesQty>10</salesQty>
<prodName>Flamingo Towels</prodName>
<prodDesc>Terry towling material printed with flamingos</prodDesc>
<prodPrice>$15.00</prodPrice>
<extendedPrice>$150.00</extendedPrice>
</lines_rec>
</lines>
<bottom QUERY="SELECT i.invoiceNum, Sum(l.salesQty * p.prodPrice) AS subtotal FROM invoice i
INNER JOIN (lines l INNER JOIN products p ON l.prodCode = p.prodCode) ON
i.invoiceNum = l.invoiceNum
GROUP BY i.invoiceNum">
<bottom_rec>
<invoiceNum>102</invoiceNum>
<subtotal>$2,459.00</subtotal>
</bottom_rec>
<bottom_rec>
<invoiceNum>103</invoiceNum>
<subtotal>$2,026.00</subtotal>
</bottom_rec>
</bottom>
</database>

Here is the XSL style sheet with which I am working:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="
<xsl:eek:utput method="html" indent="yes"/>
<!-- Main rule -->
<xsl:template match="/">
<html>
<head>
<title>
Invoice
</title>
</head>
<body bgcolor="#FFFFFF">

<H2 align="right"><font color="blue">INVOICE</font></H2>
<table cellpadding="5" width="100%">
<tr valign="top">
<td width="15%">
<font color="blue">Ship To:</font>
</td>
<td width="35%">
<font size="-1">
<xsl:apply-templates select="*/*/invoices_rec"/>
</font>
</td>
<td width="15%">
<font color="blue">Bill To:</font>
</td>
<td width="35%">
<font size="-1">
<xsl:apply-templates select="*/*/billTo_rec"/>
</font>
</td>
</tr>
</table>
<p/>
<table border="1" bordercolor="blue" width="100%">
<tr bgcolor="blue">
<xsl:apply-templates select="*/orderec"/>
</tr>
<tr>
<xsl:apply-templates select="*/*/orderec_rec"/>
</tr>
</table>
<p/>
<table border="0" cellpadding="2" width="100%">
<xsl:apply-templates select="*/products[position()=1]"/>
<xsl:apply-templates select='/database/products/products_rec'/>
</table>
<hr size="3"/>
<div align="right">
<table width="40%">
<xsl:for-each select="*/buttom/*/*">
<tr>
<td>
<font color="blue"><xsl:value-of select="@NAME"/>:</font>
</td>
<td align="right">
<xsl:apply-templates/>
</td>
</tr>
</xsl:for-each>
</table>
</div>

</body>
</html>
</xsl:template>

<!-- Left header -->
<xsl:template match="invoices_rec">
<xsl:for-each select="./*">
<xsl:apply-templates/>
<br/>
</xsl:for-each>
</xsl:template>

<!-- Right header -->
<xsl:template match="shipTo_rec">
<xsl:for-each select="./*">
<xsl:apply-templates/>
<br/>
</xsl:for-each>
</xsl:template>

<!-- Main body for order -->
<xsl:template match="orderec_rec">
<xsl:for-each select="./*">
<td align="center">
<font color="black" size="-1">
<xsl:if test="@ISNULL">
&#160;
</xsl:if>
<xsl:apply-templates/>
</font>
</td>
</xsl:for-each>
</xsl:template>

<!-- Main header for order -->
<xsl:template match="orderec">
<xsl:for-each select="./*/*">
<td align="center">
<font color="white" size="-1">
<xsl:value-of select="@NAME"/>:
</font>
</td>
</xsl:for-each>
</xsl:template>

<!-- Main Table -->
<xsl:template match="products">
<xsl:for-each select="./*[position()=1]">
<!-- Main Table header -->
<tr bgcolor="blue">
<xsl:for-each select="./*">
<td align="center">
<font color="white" size="-1">
<xsl:value-of select="@NAME"/>:
</font>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>

<xsl:template match="products_rec">
<tr>
<xsl:for-each select="./*">
<td align="left">
<font color="black" size="-1">
<xsl:if test="@ISNULL">
&#160;
</xsl:if>
<xsl:apply-templates/>
</font>
</td>
</xsl:for-each>
</tr>
</xsl:template>

</xsl:stylesheet>

Again, the XSL sheet is not written for the exact same data set, but should require only minor tweaking.

It seems that I should use a <xsl:for-each select="/*/*/invoice_rec> right after the <body> tag, but when I do that, instead of displaying multiple invoices nothing is displayed.

I would greatly appreciate a little insight into this. Thanks in a advance!
 
Hi Zaichik,
I had a look at your xsl, but it doesn't make sense: it refers to nodes (billTo_rec) and attributes (@NAME) that don't exist in your xml-source.
 
Hi Jel,

Thanks for your reply. Yes, the XSL doc is a template I grabbed that is similar but different. It will need some adjusting, like removing the billTo_rec (which is not in my XML) and renaming some stuff so that nodes that I do have get displayed.

Essentially, for each invoice, I want to display a header with shipping info, then a detail section with each item on the invoice, and then a footer that has the totals.

Thanks again for any help!
 
OK, I'm working on it, just a little patience ;-)
 
Here you are.
Just an example, but the idea is simple:
put the 'primary key' in a variable, and use it in x-path expressions to 'filter' the records in other 'tables'.

Code:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
   <xsl:output method="html" indent="yes" />

   <xsl:template match="/">
      <html>
         <head>
            <title>Invoice</title>
         </head>

         <body bgcolor="#FFFFFF">
            <H2 align="right">
               <font color="blue">INVOICE</font>
            </H2>

			<xsl:for-each select="//invoices_rec">
				<xsl:variable name ="invoicenum" select="invoiceNum"/>
	             <table cellpadding="5" width="100%">
	               <tr valign="top">
	                  <td width="15%">
	                     <font color="blue">Ship To:</font>
	                  </td>
	                  <td width="35%">
	                     <font size="-1">
	                        <xsl:call-template name="DisplayFields"/>
	                     </font>
	                  </td>
	                  <td width="15%">
	                     <font color="blue">Bill To:</font>
	                  </td>
	                  <td width="35%">
	                     <font size="-1">
	                        <xsl:text>Don't know what to display</xsl:text>
	                     </font>
	                  </td>
	               </tr>
	            </table>
	            <p />
	            <table border="1" bordercolor="blue" width="100%">
	                  <xsl:apply-templates select="//lines_rec[invoiceNum=$invoicenum]" />
	            </table>
	            <p />
	            <table width="40%">
	               <xsl:apply-templates select="//bottom_rec[invoiceNum=$invoicenum]" />
	         	</table>
			</xsl:for-each>            
         </body>
      </html>
   </xsl:template>


	<xsl:template name ="DisplayFields">
	<!-- as I don't know what labels to display, just take the nodename -->
		<xsl:for-each select="*[position()!=1]">
			<xsl:value-of select= "name()"/>
			<xsl:text>: </xsl:text>
			<xsl:value-of select= "."/>
			<br/>
		</xsl:for-each>
	</xsl:template>

	<xsl:template match="lines_rec">
		<xsl:if test="position()=1">
			<tr bgcolor="blue">
				<xsl:for-each select="*[position()!=1]">
		        	<td align="center">
	    	        	<font color="white" size="-1">
							<xsl:value-of select="name()"/>
						</font>
					</td>
				</xsl:for-each>
			</tr>
		</xsl:if>
		<tr>
			<xsl:for-each select="*[position()!=1]">
				<td align="center">
            		<font color="black" size="-1">
						<xsl:value-of select="."/>
					</font>
				</td>
			</xsl:for-each>
		</tr>
	</xsl:template>

	<xsl:template match="bottom_rec">
		<tr>
			<td>subtotal</td>
			<td>
				<xsl:value-of select = "subtotal"/>
			</td>
		</tr>
	</xsl:template>

</xsl:stylesheet>
 
Hi Jel,

Excellent, excellent, excellent!

Now, if I wanted to modify the DisplayFields template so that not all nodes were displayed, but just (for example) the address fields, how would I modify the template?

Code:
<xsl:template name ="DisplayFields">
    <xsl:for-each select="*[position()!=1]">
<!-- the following line would have to go?  Or is there some way I could use xsl:if or something? -->
        <xsl:value-of select= "."/>
        <br/>
    </xsl:for-each>
</xsl:template>

Thanks again for all your help!
 
You could say: "parse all nodes, except..."
Code:
    <xsl:template name ="DisplayFields">
    <!-- as I don't know what labels to display, just take the nodename -->
        <xsl:for-each select="*[name()!='invoiceNum' and name()!='shipToAddress']">
            <xsl:value-of select= "name()"/>
            <xsl:text>: </xsl:text>
            <xsl:value-of select= "."/>
            <br/>
        </xsl:for-each>
    </xsl:template>

I think it is better practice to specify what you do want to parse:
Code:
    <xsl:template name ="DisplayFields">
        <xsl:text>sales-date: </xsl:text>
        <xsl:value-of select= "salesDate"/>
        <br/>
        <xsl:text>ship to address: </xsl:text>
        <xsl:value-of select= "shipToAddress"/>
        <br/>
    </xsl:template>

By the way, naming a template "DisplayFields" as I did, is not best practice either, it's just as bad calling a fuction "DoIt"...
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top