Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

making a table from XML Data using XSL

Posted on 2006-07-10
12
Medium Priority
?
475 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
Independent Software Vendors: 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!

 

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 2000 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

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Question has a verified solution.

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

I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
Many times as a report developer I've been asked to display normalized data such as three rows with values Jack, Joe, and Bob as a single comma-separated string such as 'Jack, Joe, Bob', and vice versa.  Here's how to do it. 
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …
In this video, Percona Solution Engineer Rick Golba discuss how (and why) you implement high availability in a database environment. To discuss how Percona Consulting can help with your design and architecture needs for your database and infrastr…

722 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