RXC
RXC

Reputation: 1233

Columns and Headers in XSLT

I have an XSLT file that transforms an XML file into a tab-delimited file. I have also added column headers to the file.

Is it possible to somehow get the headers lined up with the columns using XSLT?

Here is my XSLT file:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="wdexample.com" version="1.0">
   <xsl:output method="text" encoding="utf-8" media-type="text/plain" />
   <xsl:strip-space elements="*" />
   <xsl:variable name="newline" select="'&#xA;'" />
   <xsl:variable name="tab" select="'&#x9;'" />
   <xsl:template match="/">
      FAO
      <xsl:value-of select="$tab" />
      FAO_REFERENCE_ID
      <xsl:value-of select="$tab" />
      FAO_TYPE
      <xsl:value-of select="$tab" />
      COMPANY_OF_FAO_DESCRIPTOR
      <xsl:value-of select="$tab" />
      COMPANY_OF_FAO_Custom_Worktag_1_ID
      <xsl:value-of select="$newline" />
      <xsl:for-each select="/wd:Report_Data/wd:Report_Entry">
         <xsl:value-of select="substring(concat(wd:FAO, ''), 1)" />
         <xsl:value-of select="$tab" />
         <xsl:value-of select="substring(concat(wd:FAO_REFERENCE_ID, ''), 1, 8)" />
         <xsl:value-of select="$tab" />
         <xsl:value-of select="substring(concat(wd:FAO_TYPE, ''), 1)" />
         <xsl:value-of select="$tab" />
         <xsl:value-of select="substring(concat(wd:COMPANY, ''), 1)" />
         <xsl:value-of select="$tab" />
         <xsl:value-of select="substring(concat(wd:COMPANY_TYPE, ''), 1)" />
         <xsl:value-of select="$tab" />
      </xsl:for-each>
   </xsl:template>
</xsl:stylesheet>

Here is the input XML. In the XML file, there won't always be values for each Report_Entry node.

<?xml version="1.0" encoding="UTF-8"?>
<wd:Report_Data xmlns:wd="wdexample.com">
    <wd:Report_Entry>
        <wd:FAO>testFAO</wd:FAO>
        <wd:FAO_REFERENCE_ID>testRefID</wd:FAO_REFERENCE_ID>
        <wd:FAO_TYPE>testType</wd:FAO_TYPE>
        <wd:COMPANY>testCompany</wd:COMPANY>
        <wd:COMPANY_TYPE>testCompanyType</wd:COMPANY_TYPE>
    </wd:Report_Entry>
    <wd:Report_Entry>
        <wd:FAO>testFAO</wd:FAO>
        <wd:FAO_REFERENCE_ID>testRefID<</wd:FAO_REFERENCE_ID>
        <wd:FAO_TYPE></wd:FAO_TYPE>
        <wd:COMPANY></wd:COMPANY>
        <wd:COMPANY_TYPE>testCompanyType</wd:COMPANY_TYPE>
    </wd:Report_Entry>
    ...
</wd:Report_Data>

The report entry node is repeated multiple times. In case you are wondering, I have the substring(concat()) functions in there in case I have to pad columns in the future.

I am considering using padding to give columns the same width of that column title in case there is no value for the column. Is there a better way to do that?

Just a recap of my questions: 1) Is it possible to line up the column headers with the columns? 2) Is there a better way of lining up column and the headers to stay in line?

Upvotes: 1

Views: 3648

Answers (2)

RXC
RXC

Reputation: 1233

I decided to create variables for the column headers with enough spaces attached like so:

<xsl:variable name="FAO" select="concat('FAO                                               ', '')" />
<xsl:variable name="FAO1" select="concat('FAO REFERENCE ID', '')" />

I then display the column headers in the template before my for each loop.

Then in the loop, I output the values like before but I use the length of the column headers like this:

<xsl:value-of select="substring(concat(wd:FAO, '                                                  '), 1, string-length($FAO))" /><xsl:value-of select="$tab"/>
<xsl:value-of select="substring(concat(wd:FAO_REFERENCE_ID, '                                                  '), 1, string-length($FAO1))" /><xsl:value-of select="$tab"/>

It's a bit ugly but it will work until I find a better solution.

Upvotes: 0

arkonautom
arkonautom

Reputation: 958

~ no. Tab size in characters isn't standard.

It looks like all your data is consistent and doesn't vary in size (much), otherwise you would have same positioning inconsistencies between columns of different rows as you have between data and headers now.

What is the target use of the result? Will it be parsed (specific separators & structure needed) or printed (should look nice & have aligned columns)?

If you want file to look pretty I would suggest fixed-length record file, achieved by something like below.

<xsl:template match="/" name="sp-padding">
    <xsl:param name="count">0</xsl:param>
    <xsl:if test="$count > 0">
        <xsl:text>&#160;</xsl:text>
        <xsl:call-template name="sp-padding">
            <xsl:with-param name="count" select="$count - 1"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:for-each select="nodesToBeConvertedToRow">
    <!-- Col nr:  1 Length: 10-->
    <xsl:call-template name="sp-padding">
        <xsl:with-param name="count" select="10"/>
    </xsl:call-template>
    <!-- Col nr:  2 Length:  3-->
    <xsl:value-of select="nodeWithText"/>
    <xsl:call-template name="sp-padding">
        <xsl:with-param name="count" select="3 - string-length(nodeWithText)"/>
    </xsl:call-template>
    ...
    <xsl:text>&#10;</xsl:text>
</xsl:for-each>

Field data length need to be smaller then allocated record size and some others restrictions are present. It worked for my case though as all record lengths were bigger then corresponding schema field sizes.

Upvotes: 2

Related Questions