Solved

making a table from XML Data using XSL

Posted on 2006-07-10
12
468 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
  • 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
 

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
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
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

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

The Problem How to write an Xquery that works like a SQL outer join, providing placeholders for absent data on the outer side?  I give a bit more background at the end. The situation expressed as relational data Let’s work through this.  I’ve …
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, …
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

760 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

Need Help in Real-Time?

Connect with top rated Experts

24 Experts available now in Live!

Get 1:1 Help Now