Solved

making a table from XML Data using XSL

Posted on 2006-07-10
12
474 Views
Last Modified: 2008-03-06
Hi,
 I want to make a html table for this data using XSL .. Please help.
Thanks,
<Field Table="Triad" Row="1" Column="1">
              <Name>Product</Name>
              <DisplayName>Product</DisplayName>
              <DataFormat Type="String"/>
              <Data>Professional Services</Data>
            </Field>
        <Field  Table="Triad" Row="1" Column="2">
              <Name>ItemNum</Name>
              <DisplayName>Item #</DisplayName>
              <DataFormat Type="String"/>
              <Data>280-OOCN-O2</Data>
            </Field>
        <Field  Table="Triad" Row="1" Column="3">
              <Name>Quantity</Name>
              <DisplayName>Quantity</DisplayName>
              <DataFormat Type="Integer">Hours</DataFormat>
              <Data>10</Data>
            </Field>
        <Field  Table="Triad" Row="1" Column="4">
              <Name>Price</Name>
              <DisplayName>Price</DisplayName>
              <DataFormat Type="Currency">USDollar</DataFormat>
              <Option>Fixed</Option>
              <Data>500</Data>
            </Field>
        <Field  Table="Triad" Row="1" Column="5">
              <Name>Total</Name>
              <DisplayName>Total</DisplayName>
              <DataFormat Type="Currency">USDollar</DataFormat>
              <Data>5,000</Data>
            </Field>
        <Field  Table="Triad" Row="2" Column="1">
              <Name>Product</Name>
              <DisplayName>Product</DisplayName>
              <DataFormat Type="String">Class Name</DataFormat>
              <Data Type="String">Training Classes</Data>
            </Field>
        <Field  Table="Triad" Row="2" Column="2">
              <Name>ItemNum</Name>
              <DisplayName>ItemNum</DisplayName>
              <DataFormat Type="String"/>
              <Data>280-OOTR-06</Data>
            </Field>
        <Field  Table="Triad" Row="2" Column="3">
              <Name>Quanitity</Name>
              <DisplayName>Quanitity</DisplayName>
              <DataFormat Type="Integer">Classes</DataFormat>
              <Data>10</Data>
            </Field>
        <Field  Table="Triad" Row="2" Column="4">
              <Name>Price</Name>
              <DisplayName>Price</DisplayName>
              <DataFormat Type="Currency">USDollar</DataFormat>
              <Data>10,000</Data>
            </Field>
        <Field Table="Triad" Row="2" Column="5">
              <Name>Total</Name>
              <DisplayName>Total</DisplayName>
              <DataFormat Type="Currency">USDollar</DataFormat>
              <Data>100,000</Data>
            </Field>
0
Comment
Question by:BrijBhasin
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 5
12 Comments
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 17073680
Hi BrijBhasin,

what about this

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <html>
            <body>
                <table>
                    <tr>
                        <xsl:for-each select="//Field[@Row = '1']">
                            <th><xsl:value-of select="Name"/></th>
                        </xsl:for-each>
                    </tr>
                    <xsl:apply-templates select="//Field[not(@Row = preceding-sibling::Field/@Row)]"/>
                   
                </table>
            </body>
        </html>

    </xsl:template>
    <xsl:template match="Field">
        <xsl:param name="row" select="@Row"/>
        <tr>
            <xsl:for-each select="//Field[@Row = $row]">
                <td><xsl:value-of select="Data"/></td>
            </xsl:for-each>
        </tr>
    </xsl:template>
</xsl:stylesheet>


Cheers!
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 17073892
BrijBhasin,

some explanation...

This XSLT assumes that you have a root element around the fields
and that you nicely have the same amount of Field elements per row.
You can make this more robust by also taking the Table Name and the Column number in the processing,
so you can add empty cells if required

Basically, I create a header row by iterating over all the Field elements of Row 1

Then I push all the Field element nodes to the template for Field, that have a unique row number
inside the Field template I iterate over all the Field elements having the current row attribute
That is it

cheers
0
 

Author Comment

by:BrijBhasin
ID: 17074135
Hi Gertone,
  Thanks for the tip.. it works perfectly. However, when I run the transformation I get these empty <tr> nodes .. any idea why.
<table>
<tr>
<th>Product</th><th>ItemNum</th><th>Quantity</th><th>Price</th><th>Total</th>
</tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>

<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>
<tr></tr>

<tr></tr>
<tr></tr>
<tr>
<td>Professional Services</td><td>280-OOCN-O2</td><td>10</td><td>500</td><td>5,000</td>
</tr>
<tr>
<td>Training Classes</td><td>280-OOTR-06</td><td>10</td><td>10,000</td><td>100,000</td>

</tr>
<tr></tr>
<tr></tr>
</table>
0
How to Create Failover DNS Record Sets in Route 53

Route 53 has the ability to easily configure DNS record sets specifically for failover scenarios. These failover record sets can be configured to failover to full-blown deployments in other regions or to a static HTML page that informs your customers of the issue.

 

Author Comment

by:BrijBhasin
ID: 17074221
Also,  I wanted to add a Row to then end which will have the grand total so the XML would be something like this

  <Field Table="Triad" Row="3" Column="5">
              <Name>Total</Name>
              <DisplayName>Total License and Training Fees</DisplayName>
              <DataFormat Type="Currency">USDollar</DataFormat>
              <Data>105,000</Data>
            </Field>

I would have to do a rowspan on the first 4 columns.. any ideas? .. Thanks.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 17074392
BrijBhasin,

I assume you do some other processing as well
and using an apply-templates without chielding the Field elements from processing,
you hit the template for Field on every Field, so creating the empty rows

I put a mode in, to shield the template from being activated on every Field node

I also calculated the number of cells on the first row
and if a number of cells doesn't match the number on the top row,
I add an empty cell with the appropriate colspan before it,
so that fits your Total requirement

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   
    <xsl:param name="colCount" select="count(//Field[@Row = '1'])"/>
    <xsl:template match="/">
        <html>
            <body>
                <table border="1">
                    <tr>
                        <xsl:for-each select="//Field[@Row = '1']">
                            <th><xsl:value-of select="Name"/></th>
                        </xsl:for-each>
                    </tr>
                    <xsl:apply-templates select="//Field[not(@Row = preceding-sibling::Field/@Row)]" mode="tab-proc"/>
                   
                </table>
            </body>
        </html>

    </xsl:template>
    <xsl:template match="Field" mode="tab-proc">
        <xsl:param name="row" select="@Row"/>
        <tr>
            <xsl:choose>
                <xsl:when test="count(//Field[@Row = $row]) &lt; $colCount">
                    <td colspan="{$colCount - count(//Field[@Row = $row])}" ></td>
                </xsl:when>
            </xsl:choose>
           
            <xsl:for-each select="//Field[@Row = $row]">
                <td><xsl:value-of select="Data"/></td>
            </xsl:for-each>
        </tr>
    </xsl:template>
</xsl:stylesheet>
0
 

Author Comment

by:BrijBhasin
ID: 17075557
I tried with the mode and it didnt work... How would do it so that you only want to process the Field Nodes with Table="MyTable".. The reason I want to do it this way is because I have a lot of field nodes all over the document and only those with a Table attribute to a specific table need to be pulled in the table.. There are alos some other field nodes which don't go in tables but need to be filted a an LI list.

Thanks.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 17076201
Hey,
I am puzzled about the mode, it should work.
How do you differentiate a Field for a li from a Field for a table?

We can organise the generation of the table based on the table attribute
but I first want to find out about other selections happening earlier

cheers

Geert
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 17076936
BrijBhasin,

here is an XSLT that goes from the assumption that only Field elements that have a Table attribute
should be listed in a table, the others are LI

All Field elements with the same Table attribute are brought together in a seperate table

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   
    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="Field[@Table and @Row='1' and @Column='1']">
           <xsl:call-template name="createRows">
                <xsl:with-param name="tabName" select="@Table"/>
            </xsl:call-template>
    </xsl:template>
   
    <xsl:template match="Field[@Table and not(@Row='1' and @Column='1')]"/>
   
    <xsl:template match="Field[not(@Table)]">
        <li><xsl:value-of select="Data"/></li>
    </xsl:template>
   
    <xsl:template name="createRows">
        <xsl:param name="tabName"/>
        <xsl:param name="colCount" select="count(//Field[@Table = $tabName and @Row = '1'])"/>
        <table border="1">
            <tr>
                <xsl:for-each select="//Field[@Table = $tabName and @Row = '1']">
                    <th><xsl:value-of select="Name"/></th>
                </xsl:for-each>
            </tr>
            <xsl:for-each select="//Field[@Table = $tabName][not(@Row = preceding-sibling::Field[@Table = $tabName]/@Row)]">
                <xsl:variable name="row" select="@Row"/>
                <tr>
                    <xsl:choose>
                        <xsl:when test="count(//Field[@Table = $tabName][@Row = $row]) &lt; $colCount">
                            <td colspan="{$colCount - count(//Field[@Table = $tabName][@Row = $row])}" ></td>
                        </xsl:when>
                    </xsl:choose>
                   
                    <xsl:for-each select="//Field[@Table = $tabName][@Row = $row]">
                        <td><xsl:value-of select="Data"/></td>
                    </xsl:for-each>
                </tr>
               
            </xsl:for-each>
        </table>
    </xsl:template>
</xsl:stylesheet>

cheers

Geert
0
 

Author Comment

by:BrijBhasin
ID: 17083750
Thanks Geert .. that works but I need different formatting for different tables in the document so I want to pick a set of Field Nodes based on the Table attribute and process them a little differently than another set. Also not all Field Nodes are enclosed in element "Term" and I don't want to process all the Field Nodes but only one that are in a specific "Term". Please let me know if I'm asking to much in the this question, I would gladly open another question. Here is how my XML structure looks something like this.

<?xml version="1.0" encoding="UTF-8"?>
<Contract>
<Section>
    <name>Section A</name>
    <Term Type="List">
        <name>List X</name>
        <Field></Field>
        <Field></Field>
        <Field></Field>
    </Term>
</Section>
<Section>
    <name>Section B</name>
    <Term Type="Table">
        <name>Table Y</name>
        <Field></Field>
        <Field></Field>
        <Field></Field>
    </Term>
</Section>
</Contract>

0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 500 total points
ID: 17085782
You can easily organise the differentiation for other Field consitions yourself
some examples

- If you want to delete all the Field elements that don't have a Term parent
<xsl:template match="Field[not(parent::Term)]"/>

-If you want to create List Items only when there is a List type on the Term
   <xsl:template match="Field[parent::Term/@Type = 'List']">
        <li><xsl:value-of select="Data"/></li>
    </xsl:template>

- for different processing of a specific table name yu can use a choose and create an extra named template
or change the template to be selective as in this example
    <xsl:template name="createRows">
        <xsl:param name="tabName"/>
        <xsl:param name="colCount" select="count(//Field[@Table = $tabName and @Row = '1'])"/>
        <table>
            <xsl:choose>
               <xsl:when test="$tabName = 'mySpecialName'"><xsl:attribute name="border" select="'2'"/></xsl:when>
               <xsl:otherwise><xsl:attribute name="border" select="'1'"/></xsl:otherwise>
            </xsl:choose>
            <tr>
                <xsl:for-each select="//Field[@Table = $tabName and @Row = '1']">
                    <th><xsl:value-of select="Name"/></th>
                </xsl:for-each>
            </tr>
            <xsl:for-each select="//Field[@Table = $tabName][not(@Row = preceding-sibling::Field[@Table = $tabName]/@Row)]">
                <xsl:variable name="row" select="@Row"/>
                <tr>
                    <xsl:choose>
                        <xsl:when test="count(//Field[@Table = $tabName][@Row = $row]) &lt; $colCount">
                            <td colspan="{$colCount - count(//Field[@Table = $tabName][@Row = $row])}" ></td>
                        </xsl:when>
                    </xsl:choose>
                   
                    <xsl:for-each select="//Field[@Table = $tabName][@Row = $row]">
                        <td><xsl:value-of select="Data"/></td>
                    </xsl:for-each>
                </tr>
               
            </xsl:for-each>
        </table>
    </xsl:template>

As you see, if the table name is 'mySpecialName' then the border will be double
you can put this choose tests anywhere in the template to make a different table layout

hope this is clear enough

cheers

Geert
0
 

Author Comment

by:BrijBhasin
ID: 17086422
The XSL keeps pulling up all the extra junk nodes in the doc, how do I prevent that.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 17087934
> the extra junk nodes

that means you have an apply templates somewhere that pushes out these nodes
try to find out where you push out the nodes you don't want, and be selective
or create empty templates for the nodes you don't want

are there Field elements pulled up that you don't want?
look at all the Field templates,
you might have to remove some

cheers
0

Featured Post

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!

Question has a verified solution.

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

The Client Need Led Us to RSS I recently had an investment company ask me how they might notify their constituents about their newsworthy publications.  Probably you would think "Facebook" or "Twitter" but this is an interesting client.  Their cons…
The Confluence of Individual Knowledge and the Collective Intelligence At this writing (summer 2013) the term API (http://dictionary.reference.com/browse/API?s=t) has made its way into the popular lexicon of the English language.  A few years ago, …
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Sometimes it takes a new vantage point, apart from our everyday security practices, to truly see our Active Directory (AD) vulnerabilities. We get used to implementing the same techniques and checking the same areas for a breach. This pattern can re…

630 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