Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

making a table from XML Data using XSL

Posted on 2006-07-10
12
Medium Priority
?
479 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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 

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

How to Use the Help Bell

Need to boost the visibility of your question for solutions? Use the Experts Exchange Help Bell to confirm priority levels and contact subject-matter experts for question attention.  Check out this how-to article for more information.

Question has a verified solution.

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

Browsing the questions asked to the Experts of this forum, you will be amazed to see how many times people are headaching about monster regular expressions (regex) to select that specific part of some HTML or XML file they want to extract. The examp…
Create a Windows 10 custom Image with custom task bar and custom start menu using XML for deployment.
Integration Management Part 2
When cloud platforms entered the scene, users and companies jumped on board to take advantage of the many benefits, like the ability to work and connect with company information from various locations. What many didn't foresee was the increased risk…
Suggested Courses

824 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