0

I have xml with the following data.

<Rows>
   <Header>
      <sourcetable>Table_1</sourcetable>
      <targettable>Table_2</targettable>
   </Header>
   <Table>
      <Source_Fieldname>DTIME_INSERTED</Source_Fieldname>
      <Source_Type>Timestamp</Source_Type>
      <Source_Fieldname>ID_JOB</Source_Fieldname>
      <Source_Type>String</Source_Type>
   </Table>

   <Header>
      <sourcetable>Table_3</sourcetable>
      <targettable>Table_4</targettable>
   </Header>
   <Table>
      <Source_Fieldname>DTIME_INSERTED</Source_Fieldname>
      <Source_Type>Timestamp</Source_Type>
      <Source_Fieldname>ID_JOB</Source_Fieldname>
      <Source_Type>String</Source_Type>
   </Table>   
</Rows>

I am trying to output this into separate tables per "Table" element just like this but can't figure it out since there are multiple elements with the same name.
Table_

So far this is what I got.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" indent="yes" />
    <xsl:template match="Row">        

        <table border="1px" cellpadding="3" cellspacing="1" style='font-family:"Calibri"; font-size:12; font-weight:"normal"' >
            <tr align="center">
            <xsl:for-each select="preceding-sibling::Header[1]">
               <th colspan="4"  bgcolor="#ccffff">
               <font size="2" face="Calibri" style="text-transform:uppercase"> <xsl:value-of select="sourcetable" /> </font> 
               </th>
               <th colspan="4" bgcolor="#ccffff">
               <font size="2" face="Calibri" style="text-transform:uppercase"> <xsl:value-of select="targettable" /> </font>
               </th>
            </xsl:for-each>
                <th bgcolor="#FFCCBC" rowspan="2">Flagfield</th>
            </tr>
            <tr  align="left">
                <td bgcolor="#C5E1A5">Source_Fieldname</td>
                <td bgcolor="#C5E1A5">Source_Type</td>
            </tr>

            <xsl:for-each select=".">

             <tr>
                <xsl:for-each select="Source_Fieldname">
                    <td> <xsl:value-of select="."/> </td>
                </xsl:for-each>

                <xsl:for-each select="Source_Type">
                        <td> <xsl:value-of select="."/> </td>
                </xsl:for-each>
            </tr>   

            </xsl:for-each>

            </xsl:for-each>
        </table>
        <br /><br/>
    </xsl:template>

</xsl:stylesheet>

Any suggestion how to achieve desired output is appreciated. Thanks!

2 Answers 2

1

You had a good start but a few details were not working. Here's how I would solve it.

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

    <xsl:template match="/">
        <html>
            <head/>
            <body>
                <xsl:apply-templates select="Rows/Table"/>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="Table">
        <table border="1px" cellpadding="3" cellspacing="1" style='font-family:"Calibri"; font-size:12; font-weight:"normal"' >
            <tr align="center">
                <th bgcolor="#ccffff">
                    <font size="2" face="Calibri" style="text-transform:uppercase"> <xsl:value-of select="preceding-sibling::Header[1]/sourcetable" /> </font> 
                </th>
                <th bgcolor="#ccffff">
                   <font size="2" face="Calibri" style="text-transform:uppercase"> <xsl:value-of select="preceding-sibling::Header[1]/targettable" /> </font>
                </th>
            </tr>
            <tr  align="left">
                <td bgcolor="#C5E1A5">Source_Fieldname</td>
                <td bgcolor="#C5E1A5">Source_Type</td>
            </tr>
            <xsl:apply-templates select="Source_Fieldname"/>
        </table>
        <br/><br/>
    </xsl:template>

    <xsl:template match="Source_Fieldname">
        <tr>
            <td> <xsl:value-of select="."/> </td>
            <td> <xsl:value-of select="following-sibling::Source_Type[1]"/> </td>
        </tr>   
    </xsl:template>

</xsl:stylesheet>

You can test it here : https://xsltfiddle.liberty-development.net/bFWR5Ej/1

Sign up to request clarification or add additional context in comments.

2 Comments

thanks! You have the same solution as @zx485 tho I've already accepted his as correct answer.
I don't have enough reputation points to comment on the answer you accepted. There are 2 things to note that should be changed in that solution :<xsl:for-each select="preceding-sibling::Header[1]"> : preceding-sibling::Header[1] always returns only one Header, so why loop on that? Also <xsl:for-each select="*[local-name()='Source_Fieldname']"> could simply be replaced by : <xsl:for-each select="Source_Fieldname">. I don't see why a name comparison is needed there.
0

So far I managed to create the desired outcome, but however, I would seriously suggest to sanitize your XML file/input, because it's complicating things.

But with your current file, you can use this XSLT-1.0 stylesheet to get the desired output:

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

    <!-- A template handling the main HTML stuff -->
    <xsl:template match="/Rows">
        <html>
            <body>
                <font size="2" face="Calibri" >
                    <h1>The following Source - Target Tables have DDL Mismatch(es)</h1>
                    <br />
                    <xsl:apply-templates select="Table" />
                </font>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="Table">        
        <table border="1px" cellpadding="3" cellspacing="1" style='font-family:"Calibri"; font-size:12; font-weight:"normal"' >
            <tr align="center">
            <xsl:for-each select="preceding-sibling::Header[1]">
               <th colspan="1"  bgcolor="#ccffff">
               <font size="2" face="Calibri" style="text-transform:uppercase"> <xsl:value-of select="sourcetable" /> </font> 
               </th>
               <th colspan="1" bgcolor="#ccffff">
               <font size="2" face="Calibri" style="text-transform:uppercase"> <xsl:value-of select="targettable" /> </font>
               </th>
            </xsl:for-each>
                <th bgcolor="#FFCCBC" rowspan="2">Flagfield</th>
            </tr>
            <tr  align="left">
                <td bgcolor="#C5E1A5">Source_Fieldname</td>
                <td bgcolor="#C5E1A5">Source_Type</td>
            </tr>
            <xsl:for-each select="*[local-name()='Source_Fieldname']">
                <tr>
                    <td><xsl:value-of select="."/></td>
                    <td><xsl:value-of select="following-sibling::Source_Type[1]"/></td>
                </tr>   
            </xsl:for-each>
        </table>
        <br /><br/>
    </xsl:template>

</xsl:stylesheet>

The central aspect is the xsl:for-each loop:

<xsl:for-each select="*[local-name()='Source_Fieldname']">
    <tr>
        <td><xsl:value-of select="."/></td>
        <td><xsl:value-of select="following-sibling::Source_Type[1]"/></td>
    </tr>   
</xsl:for-each>

The output is:

enter image description here

It simply iterates over all <Table> children which are named Source_Fieldname and then creates a table cell for these and for the first following-sibling Source_Type element. This does work for a two element table, but it should be no problem to extend it to a several children situation.

4 Comments

you really is a lifesaver! I further understood how your code works because of the explanation. I'm trying to think on how to sanitize the xml. I can edit how it is generated. If you have suggestions I'm open for it :)
Thanks. And, well, the suggestion for improvement should be evident: Just split your element names by the underscore: So group Source_Fieldname and Source_Type elements in a Source element. Then group Fieldname elements and Type elements into separate groups. Do the same for Target_* elements. After doing that, you can access those elements easily by index and can omit the following::... syntax and similar hacks. This should simplify your XML.
awesome. working on it. quick question tho. If i move the <Header>s inside <Table> what would I replace in the xsl?
You do not have to move the <Header>s into the <Table>s. The purpose of XML is to dissect the data from the output, so your attempt at formatting your XML like your desired output HTML is not necessarily the best approach. I do not try to sell you my best approach of managing your data either, because (and this should be obvious) I do not know the structure of your data. So I cannot give you the 'solve it all' solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.