I have the following xml / xsl:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="./test.xsl"?>
<a>
<b>
<c >
<no>1</no>
<d>
<e>
<f>20060603190000</f>
</e>
</d>
</c>
<c >
<no>1</no>
<d>
<e>
<f>20060603190000</f>
</e>
</d>
</c>
<c>
<no>2</no>
<d>
<e>
<f>20060603190000</f>
</e>
</d>
</c>
<c >
<no>1</no>
<d>
<e>
<f>20060819200000</f>
</e>
<e>
<f>20060902200000</f>
</e>
</d>
</c>
<c >
<no>1</no>
<d>
<e>
<f>20070819200000</f>
</e>
<e>
<f>20070819200003</f>
</e>
<e>
<f>20070819200001</f>
</e>
<e>
<f>20060903100000</f>
</e>
</d>
</c>
<c >
<no>1</no>
<d>
<e>
<f>20060819200000</f>
</e>
<e>
<f>20060902200000</f>
</e>
</d>
</c>
</b>
</a>
<?xml version="1.0" encoding="ISO-8859-15"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" >
<xsl:output method="text" indent="yes" />
<xsl:template match="/">
<table>
<tr>
<xsl:for-each select="a/b/c[no=1]/d/e/f[not(.=preceding::*)]" ><xsl:sort select="." data-type="number"/>
<td><xsl:value-of select="."/></td>
</xsl:for-each>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
when applied, the result looks like this:
20060603190000
20060819200000
20060902200000
20060903100000
20070819200000
20070819200001
20070819200003
which is partly correct, since:
i want to display distinct values of f (which works)
i want to sort the values of f (which works too)
but:
i want to display values that are distinct for the first 8 digits only (year, month, day), so that the result would be:
20060603190000
20060819200000
20060902200000
20060903100000
20070819200000
ie values with the same date but different time are seen as equal.
I have tried to use substring(values, 8) in different places, but i can't get it to work that way nor using key/generate-id.
Can anyone please give me advise on that?
edited:
i have solved the problem on my own.
the following template create a string containing all dates sorted by time and truncated to the first 8 digits (the date withaout the time):
<xsl:template match="/">
<xsl:variable name="text">
<xsl:for-each select="a/b/c[no=1]/d/e/f[not(.=preceding::*)]" ><xsl:sort select="." data-type="number"/>
<xsl:value-of select="substring(.,0,9)"/>-</xsl:for-each>
</xsl:variable>
text:<xsl:value-of select="$text"/>
<xsl:call-template name="filter"><xsl:with-param name="text"><xsl:value-of select="$text"/></xsl:with-param><xsl:with-param name="previousElement"/></xsl:call-template >
</xsl:template>
output example: 20060603-20060819-20060902-20060902-20060902-20060903-20070819-20070819-20070819-20110902-20110902-
then a second template is called which outputs the distinct values by recursivly calling itself:
<xsl:template name="filter">
<xsl:param name="text"/>
<xsl:param name="previousElement"/>
<xsl:variable name="firstElement"><xsl:value-of select="substring-before($text, '-')"/></xsl:variable>
<xsl:variable name="rest"><xsl:value-of select="substring-after($text, '-')"/></xsl:variable>
<!-- firstElement:<xsl:value-of select="$firstElement"/>
previousElement:<xsl:value-of select="$previousElement"/>
rest:<xsl:value-of select="$rest"/>
-->
<xsl:if test="string-length($firstElement) > 0">
<xsl:choose>
<xsl:when test="$firstElement = $previousElement">
<xsl:call-template name="filter"><xsl:with-param name="text"><xsl:value-of select="$rest"/></xsl:with-param><xsl:with-param name="previousElement"><xsl:value-of select="$firstElement"/></xsl:with-param></xsl:call-template >
</xsl:when>
<xsl:otherwise>
output:<xsl:value-of select="$firstElement"/>
<xsl:call-template name="filter"><xsl:with-param name="text"><xsl:value-of select="$rest"/></xsl:with-param><xsl:with-param name="previousElement"><xsl:value-of select="$firstElement"/></xsl:with-param></xsl:call-template >
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
i answered my question myself as seen above. according to the advice of Marcus Rickert i will answer it this way and accept it to make it closable and maybe helpful for others.
Related
I have XML form data coming in from a vendor app that allows me to select values for fields on the form by using this format.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="text" /><xsl:template match="/"><xsl:apply-templates/></xsl:template>
<xsl:template match="DataSet/diffgr:diffgram/Forms">
<xsl:for-each select="Form">
<xsl:value-of select="FieldName1"/>
<xsl:value-of select="FieldName2"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I can't see the XML, but I can surmise from this that all of the fields are siblings within the <form> and looks something like this:
<form>
<CosignerName1 value="John Doe"/>
<CosignerDate1 value="1/1/1970"/>
<CosignerTime1 value="8:46 PM"/>
<CosignerName2 value="Jane Smith"/>
<CosignerDate2 value="2/2/1972"/>
<CosignerTime2 value="11:46 AM"/>
...
<CosignerName12 value="Will Hunting"/>
<CosignerDate12 value="12/12/1982"/>
<CosignerTime12 value="1:00 AM"/>
</form>
<form>
<CosignerName1 value="Bill Thomas"/>
<CosignerDate1 value="5/5/2020"/>
<CosignerTime1 value="8:46 PM"/>
<CosignerName2 value="Bev Poole"/>
<CosignerDate2 value="6/6/2022"/>
<CosignerTime2 value="11:46 AM"/>
...
<CosignerName12 value="Bob Ross"/>
<CosignerDate12 value="12/12/1982"/>
<CosignerTime12 value="1:00 AM"/>
</form>
Known factors:
Each form has 12 cosigners, each with numbered field names i.e. CosignerName1, CosignerName2, etc.
Each named cosigner also has a CosignerDate and a CosignerTime field associated by number. i.e. CosignerName1 has CosignerDate1 and CosignerTime1 fields that, when combined, show when that user signed the form.
I am using the word 'idnum' in my code and in this question to refer to the number at the end of each label that shows they are part of a set i.e CosignerName1, CosignerDate1, and CosignerTime1 all share idnum=1
Goal: Identify and select only the most recent CosignerName, based on CosignerDate and CosignerTime.
To do this, I figure I need to:
Find the most recent CosignerDate/CosignerTime combination
Read the label of either of the identified fields to extract the idnum from the end
Use the idnum to to identify the correct CosignerName and output it
I have some pieces of the solution, but no way to bring them together:
Create a Timestamp
I can combine a set of CosignerDate[idnum] and CosignerTime[idnum] fields, then translate them into a timestamp. If I could create an array of these timestamps, then I could sort and find the most recent one, but as I understand it XSLT 1.0 doesn't have arrays. So I am not sure what to do with this.
<xsl:variable name="mm" select="substring-before(CosignerDate1,'/')" />
<xsl:variable name="mmyyyy" select="substring-after(CosignerDate1,'/')"/>
<xsl:variable name="dd" select="substring-before($mmyyyy,'/')" />
<xsl:variable name="yyyy" select="substring-after($mmyyyy,'/')" />
<xsl:variable name="ampm" select="substring-after(CosignerTime1,' ')" />
<xsl:variable name="time">
<xsl:choose>
<xsl:when test="$ampm = 'AM'">
<xsl:value-of select="number(translate(substring-before(CosignerTime1,' '),':',''))" />
</xsl:when>
<xsl:when test="$ampm = 'PM'">
<xsl:value-of select="number(translate(substring-before(CosignerTime1,' '),':',''))+number('1200')" />
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="concat('Timestamp:',$yyyy,$mm,$dd,$time)"/>
The code above outputs something like: Timestamp:202209191128
Get idnums
I can loop through all of the Cosigners and extract the identifying number shared by their labels.
<xsl:for-each select="*[starts-with(local-name(), 'CosignerName')]">
<xsl:variable name="idnum">
<xsl:value-of select="translate(local-name(),'CosignerName','')" />
</xsl:variable>
<xsl:value-of select="$idnum"/>
<xsl:value-of select="','"/>
</xsl:for-each>
This code above outputs something like: 1,4,7,12
But, this is where I get lost.
Failed Attempt
I tried something like the code below. The logic was to loop through each Cosigner name (12 iterations), get the idnum from the label, use that to get the CosignerDate and CosignerTime, create a timestamp, then sort the timestamps, if the timestamp we are looking at is the top one (most recent) then use the idnum to select the correct CosignerName and output it.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="text" /><xsl:template match="/"><xsl:apply-templates/></xsl:template>
<xsl:template match="DataSet/diffgr:diffgram/Forms">
<xsl:for-each select="Form">
<xsl:for-each select="*[starts-with(local-name(), 'CosignerName')]">
<xsl:variable name="idnum">
<xsl:value-of select="translate(local-name(),'CosignerName','')" />
</xsl:variable>
<xsl:variable name="mm" select="substring-before(concat(CosignerDate,$idnum),'/')" />
<xsl:variable name="mmyyyy" select="substring-after(concat(CosignerDate,$idnum),'/')"/>
<xsl:variable name="dd" select="substring-before($mmyyyy,'/')" />
<xsl:variable name="yyyy" select="substring-after($mmyyyy,'/')" />
<xsl:variable name="ampm" select="substring-after(concat(CosignerTime,$idnum),' ')" />
<xsl:variable name="time">
<xsl:choose>
<xsl:when test="$ampm = 'AM'">
<xsl:value-of select="number(translate(substring-before(concat(CosignerTime,$idnum),' '),':',''))" />
</xsl:when>
<xsl:when test="$ampm = 'PM'">
<xsl:value-of select="number(translate(substring-before(concat(CosignerTime,$idnum),' '),':',''))+number('1200')" />
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="timestamp" select="concat('Timestamp: ',$yyyy,$mm,$dd,$time)"/>
<xsl:sort select="$timestamp" data-type="number" order="descending"/>
<xsl:if test="position() = 1"><xsl:value-of select="."/></xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This of course fails for several reasons:
the concat with the $idnum doesn't actually pull values from fields
the sort has to be the first thing in the for-each
the sort has to happen after all of the timestamps are created
the sorted list of timestamps still needs to reference which idnum they were generated from
Without being able to create an array of timestamps and idnums, I don't know how you can accomplish this.
Does anyone know how can I get at the CosignerName[idnum] that is correlated with the most recent CosignerDate[idnum]/CosignerTime[idnum]?
I believe you could just sort the CosignerNameX nodes by the individual components of their associated dates and times, as shown in the two other answers I linked to:
https://stackoverflow.com/a/59288030/3016153
https://stackoverflow.com/a/30631073/3016153
However, it might be more efficient - and certainly more readable - to do this in two steps:
Construct a dateTime value for each cosigner;
Find the cosigner with the most recent value.
Consider the following example:
XML
<DataSet xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<diffgr:diffgram>
<Forms>
<form>
<CosignerName1 value="John Doe"/>
<CosignerDate1 value="1/1/1970"/>
<CosignerTime1 value="8:46 PM"/>
<CosignerName2 value="Jane Smith"/>
<CosignerDate2 value="2/2/1972"/>
<CosignerTime2 value="11:46 AM"/>
<CosignerName12 value="Will Hunting"/>
<CosignerDate12 value="12/12/1982"/>
<CosignerTime12 value="1:00 AM"/>
</form>
<form>
<CosignerName1 value="Bill Thomas"/>
<CosignerDate1 value="5/5/2020"/>
<CosignerTime1 value="8:46 PM"/>
<CosignerName2 value="Bev Poole"/>
<CosignerDate2 value="6/6/2022"/>
<CosignerTime2 value="11:46 AM"/>
<CosignerName12 value="Bob Ross"/>
<CosignerDate12 value="12/12/1982"/>
<CosignerTime12 value="1:00 PM"/>
</form>
</Forms>
</diffgr:diffgram>
</DataSet>
XSLT 1.0 + EXSLT node-set()
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="diffgr exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/DataSet">
<output>
<xsl:for-each select="diffgr:diffgram/Forms/form">
<xsl:variable name="datetimes">
<xsl:for-each select="*[starts-with(local-name(), 'CosignerName')]">
<!-- identify values -->
<xsl:variable name="i" select="substring-after(local-name(), 'CosignerName')" />
<xsl:variable name="date" select="../*[local-name()=concat('CosignerDate', $i)]/#value" />
<xsl:variable name="time" select="../*[local-name()=concat('CosignerTime', $i)]/#value" />
<!-- extract date components -->
<xsl:variable name="year" select="substring-after(substring-after($date, '/'), '/')" />
<xsl:variable name="month" select="substring-before($date, '/')" />
<xsl:variable name="day" select="substring-before(substring-after($date, '/'), '/')" />
<!-- extract time components -->
<xsl:variable name="hour12" select="substring-before($time, ':')" />
<xsl:variable name="minute" select="substring-before(substring-after($time, ':'), ' ')" />
<xsl:variable name="pm" select="contains($time,'PM')" />
<xsl:variable name="hour" select="$hour12 mod 12 + 12*$pm"/>
<!-- construct dateTime -->
<datetime cosigner="{#value}" index="{$i}">
<xsl:value-of select="$year"/>
<xsl:value-of select="format-number($month, '-00')"/>
<xsl:value-of select="format-number($day, '-00')"/>
<xsl:value-of select="format-number($hour, 'T00')"/>
<xsl:value-of select="format-number($minute, ':00')"/>
<xsl:text>:00</xsl:text>
</datetime>
</xsl:for-each>
</xsl:variable>
<!-- output -->
<form>
<xsl:for-each select="exsl:node-set($datetimes)/datetime">
<xsl:sort select="." data-type="text" order="descending"/>
<xsl:if test="position()=1">
<last-cosigner index="{#index}" dateTime="{.}" >
<xsl:value-of select="#cosigner"/>
</last-cosigner>
</xsl:if>
</xsl:for-each>
</form>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<form>
<last-cosigner index="12" dateTime="1982-12-12T01:00:00">Will Hunting</last-cosigner>
</form>
<form>
<last-cosigner index="2" dateTime="2022-06-06T11:46:00">Bev Poole</last-cosigner>
</form>
</output>
P.S. With a Microsoft processor you may need to change the namespace declaration:
xmlns:exsl="http://exslt.org/common"
to:
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
and then change the exsl prefix in lines #5 and #39 to msxsl.
I have this SaxonApiException when I run my XSLT code on https://xslttest.appspot.com/. It return this error :
net.sf.saxon.s9api.SaxonApiException: Errors were reported during stylesheet compilation
I tried on another online tester https://www.freeformatter.com/xsl-transformer.html but I got the same error.
I tried to split my XSLT code. First part with the process of extract ZipCode in Wages and second part with the process of extract ZipCode in Addresses.
Both works when they're separated so I think I made a mistake in the 'choose' element but cannot find it.
Here is my XSLT code...
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/EmployeeUDM_Response/Return/Employee">
<xsl:for-each select="./Wages/Wage">
<xsl:choose>
<xsl:when test="DissimelarZipCode != ''">
<xsl:value-of select="DissimelarZipCode" />
</xsl:when>
<otherwise>
<xsl:for-each select="./Addresses/Address" />
<!-- year -->
<xsl:sort select="substring(StartDate, 1, 4)" order="descending" data-type="number"/>
<!-- month -->
<xsl:sort select="substring(StartDate, 6, 2)" order="descending" data-type="number"/>
<!-- day -->
<xsl:sort select="substring(StartDate, 9, 2)" order="descending" data-type="number"/>
<xsl:if test="position() = 1">
<xsl:value-of select="./ZipCode" />
</xsl:if>
</otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
...and my XML file
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl"?>
<EmployeeUDM_Response xmlns:ns0="http://ESB/Schemas/v2/EmployeeUDM">
<Header Type="Employee" Source="Biztalk ESB" />
<Return>
<Employee>
<Wages>
<Wage>
<StartDate>2019-04-22T00:00:00.0000000+02:00</StartDate>
<EndDate>2019-05-01T00:00:00.0000000+02:00</EndDate>
<DissimelarZipCode>5430 NU</DissimelarZipCode>
</Wage>
</Wages>
<Addresses>
<Address>
<StartDate>2014-01-01T00:00:00.0000000+02:00</StartDate>
<EndDate></EndDate>
<ZipCode>6099 EB</ZipCode>
</Address>
<Address>
<StartDate>2015-01-01T00:00:00.0000000+02:00</StartDate>
<EndDate></EndDate>
<ZipCode>5487 YR</ZipCode>
</Address>
</Addresses>
</Employee>
</Return>
</EmployeeUDM_Response>
I expected the output of the ZipCode in Wage (5430 NU in this case) or, if ZipCode in Wage is empty, the ZipCode in Address with the latest StartDate (5487 YR in this case)
1. There should be <xsl:otherwise> instead of <otherwise>
2. <xsl:sort> should be in <xsl:for-each> .(You have ended the loop in same line)
3. To loop over Address, you will need xpath ../../Addresses/Address. Because at that time <Wage> is being processed. ( ../ will bring you up one level to parent node.)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/EmployeeUDM_Response/Return/Employee">
<xsl:for-each select="Wages/Wage">
<xsl:choose>
<xsl:when test="DissimelarZipCode != ''">
<xsl:value-of select="DissimelarZipCode" />
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="../../Addresses/Address">
<!-- year -->
<xsl:sort select="substring(StartDate, 1, 4)" order="descending"
data-type="number" />
<!-- month -->
<xsl:sort select="substring(StartDate, 6, 2)" order="descending"
data-type="number" />
<!-- day -->
<xsl:sort select="substring(StartDate, 9, 2)" order="descending"
data-type="number" />
<xsl:if test="position() = 1">
<xsl:value-of select="ZipCode" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pPzifpP
I am new to XSLT, i want to move a element with all its child elements to the root as mentioned below:
Input XML
<a name = "test">
<b name = "test1">
<c name = "test2">
<c1>Dummy</c1>
</c>
</b>
</a>
Expected:
<a name = "test">
<b name = "test1">
</b>
<c name = "test2">
<c1>Dummy</c1>
</c>
</a>
Try this. (This is called push programming.)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="UTF-8" />
<xsl:template match="a">
<xsl:copy>
<xsl:apply-templates select="#*|b"/>
<xsl:apply-templates select="b/c"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:copy>
<!-- Don't do an apply template with c in it. Just get the attributes for b. The c node pushed from the a template gets handled by the identity. -->
<xsl:apply-templates select="#*"/>
</xsl:copy>
</xsl:template>
<!-- Identity template. -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I have an XML file for which I have to frame XSL so that my ETL job can process it. Here is what i need.
Input XML
<FS_Sub_Investment_Team>
<Report Name="REPORT">
<MeetTheTeamTableHeading Label="TEAM" />
<MeetTheTeamTable>
<Rows>
<Row ManagerData="ABC EFGHI XYZ" />
<Row ManagerData="ABC PQRST XYZ" />
</Rows>
</MeetTheTeamTable>
<MeetTheTeamNote></MeetTheTeamNote>
</Report>
</FS_Sub_Investment_Team>
Current XSL:
<xsl:template match="/">
<Bio>
<BioText>
<xsl:apply-templates select="//Row/#ManagerData" mode="concat"/>
</BioText>
</Bio>
</xsl:template>
Current Output :
<Bio><BioText>ABC EFGHI XYZABC PQRST XYZ</Bio></BioText>
My expected output is: With line break (or) with space between the two element values, please give me both solutions.
<Bio><BioText>ABC EFGHI XYZ
ABC PQRST XYZ</Bio></BioText>
Try:
<xsl:template match="/">
<Bio>
<BioText>
<xsl:for-each select="//Row">
<xsl:value-of select="#ManagerData" />
<xsl:if test="position()!=last()">
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:for-each>
</BioText>
</Bio>
</xsl:template>
To insert a space instead of a new line, use:
<xsl:text> </xsl:text>
instead of:
<xsl:text>
</xsl:text>
I have the below part of my XML Document. here when i run my xslt on this XML document, the col class ... inside colgroup is getting called, colgroup is getting called but end tag of col class is not getting called. please let me know how do i close col class... the last code contains my html output of xml.
<table frame="all" width="100%">
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="18%"/>
<colspec colnum="2" colname="col2" colwidth="82%"/>
<thead>
<row>
<entry namest="col1" nameend="col2">
<para>A timeline of British Virgin Islands company law</para>
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<para>1884</para>
</entry>
<entry>
<para>Companies Act passed into law.</para>
</entry>
</row>
<row>
<entry>
<para>1984</para>
</entry>
<entry>
<para>International Business Companies Act passed into law.</para>
</entry>
</row>
<row>
<entry>
<para>2004</para>
</entry>
<entry>
<para>BVI Business Companies Act passed into law, coming into force on 1 January 2005.</para>
</entry>
</row>
<row>
<entry>
<para>2005</para>
</entry>
<entry>
<para>All three corporate statutes exist in parallel and it is possible to incorporate companies under any of them.</para>
</entry>
</row>
<row>
<entry>
<para>2006</para>
</entry>
<entry>
<para>Incorporation provisions in the International Business Companies Act and the Companies Act are repealed on 31 December 2005; the Acts remain in force but new companies may only be incorporated under the BVI Business Companies Act.</para>
</entry>
</row>
<row>
<entry>
<para>2007</para>
</entry>
<entry>
<para>International Business Companies Act repealed on 31 December 2006. Transitional provisions come into effect for companies incorporated under that act.</para>
</entry>
</row>
<row>
<entry>
<para>2009</para>
</entry>
<entry>
<para>Companies Act repealed on 31 December 2008. Transitional provisions come into effect for companies incorporated under that Act.</para>
</entry>
</row>
</tbody>
</tgroup>
</table>
and below is my xslt.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ntw="Number2Word.uri"
exclude-result-prefixes="ntw">
<xsl:variable name="ThisDocument" select="document('')"/>
<xsl:template match="/">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8"/>
<title>
<xsl:value-of select="chapter/title"/>
</title>
<link rel="stylesheet" href="er:#css" type="text/css"/>
</head>
<body>
<xsl:apply-templates/>
<hr/>
<section class="tr_footnotes">
<xsl:apply-templates select="//footnote"
mode="footnote"/>
</section>
</body>
</html>
</xsl:template>
<xsl:template match="chapter">
<section>
<div class="chapter">
<a name="BVI-CH-{#num}"/>
<xsl:variable name="cnum">
<xsl:choose>
<xsl:when test="starts-with(#num,'0')">
<xsl:value-of select="substring-after(#num,'0')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#num"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<div class="chapter-title">
<span class="chapter-num">
<xsl:value-of select="concat('Chapter ',$cnum,' ')"/>
</span>
<xsl:apply-templates select="title"/>
</div>
<xsl:apply-templates select="child::node()[not(self::title)]"/>
</div>
</section>
</xsl:template>
<xsl:template match="chapter/para">
<div class="para align-right">
<span class="format-smallcaps">Para</span>.
</div>
</xsl:template>
<!-- Index templates -->
<xsl:template name="toc" match="chapter/toc">
<div class="toc">
<xsl:call-template name="toc-part"/>
</div>
</xsl:template>
<xsl:template name="toc-part" match="chapter/toc/toc-part">
<div class="toc-part">
<xsl:call-template name="toc-div"/>
</div>
</xsl:template>
<xsl:template name="toc-div" match="chapter/toc/toc-part/toc-div">
<table class="toc-div">
<tbody>
<xsl:for-each select="current()/toc-part/toc-div/*">
<xsl:call-template name="toc-item"/>
</xsl:for-each>
</tbody>
</table>
</xsl:template>
<xsl:template name="toc-item" match="chapter/toc/toc-part/toc-div/toc-item">
<xsl:variable name="tocpg">
<xsl:value-of select="concat('#P',current()/toc-pg/text())"/>
</xsl:variable>
<xsl:variable name="tocpgtag" select="translate($tocpg,'.', '-')"/>
<xsl:variable name="chapternumber">
<!-- Get num attribute of parent node -->
<xsl:value-of select="ancestor::chapter[1]/#num"/>
</xsl:variable>
<xsl:variable name="itemlevel">
<xsl:value-of select="$ThisDocument//ntw:nums[#num=$chapternumber]/#word"/>
</xsl:variable>
<xsl:variable name="tocitemlevel">
<xsl:value-of select="concat('toc-item-', $itemlevel,'-level')"/>
</xsl:variable>
<table class="{$tocitemlevel}">
<tbody>
<tr>
<td class="toc-item-num">
<xsl:value-of select="current()/#num"/>
</td>
<td class="toc-title">
<xsl:value-of select="current()/toc-title"/>
</td>
<td class="toc-pg">
<a href="{$tocpgtag}">
<xsl:value-of select="current()/toc-pg"/>
</a>
</td>
</tr>
</tbody>
</table>
</xsl:template>
<!-- Index Templates Complete -->
<!-- Paragraph templates -->
<xsl:template name="section" match="section">
<!-- Variables-->
<xsl:variable name="classname">
<!--Get name attribute of current node -->
<xsl:value-of select="concat('section-',#level)"/>
</xsl:variable>
<xsl:variable name="chapternumber">
<!-- Get num attribute of parent node -->
<xsl:value-of select="ancestor::chapter[1]/#num"/>
</xsl:variable>
<xsl:variable name="sectnum">
<xsl:number level="any" count="section" format="1"/>
</xsl:variable>
<!--Create a string variable by concat string method -->
<xsl:variable name="sectionname">
<xsl:value-of select="concat('CH-',$chapternumber,'-SEC-0', $sectnum)"/>
</xsl:variable>
<!-- Template Content -->
<div class="{$classname}">
<a name="{$sectionname}"> </a>
<div class="section-title">
<span class="section-num">
<xsl:value-of select="#num"/>
</span>
<xsl:apply-templates select="title"/>
</div>
<xsl:apply-templates select="child::node()[not(self::title)]"/>
</div>
</xsl:template>
<xsl:template name="para" match="section/para">
<div class="para">
<xsl:apply-templates select="phrase"/>
<span class="phrase">
<xsl:value-of select="current()/phrase"/>
</span>
<xsl:apply-templates select="child::node()[not(self::phrase)]"/>
</div>
</xsl:template>
<xsl:template name="phrase" match="phrase">
<xsl:variable name="phrase">
<xsl:value-of select="concat('P',text())"/>
</xsl:variable>
<xsl:variable name="newphrase" select="translate($phrase,'.','-')"/>
<a>
<xsl:attribute name="name"><xsl:value-of select="$newphrase"></xsl:value-of></xsl:attribute>
</a>
</xsl:template>
<!-- Table Templates -->
<xsl:template name="table" match="table">
<table style="frame-{current()/#frame} width-{translate(current()/#width,'%','')}">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="tgroup">
<colgroup>
<xsl:apply-templates select=".//colspec"/>
</colgroup>
<xsl:apply-templates select="child::node()[not(self::colspec)]"/>
</xsl:template>
<xsl:template name="tbody" match="tgroup/tbody">
<tbody>
<xsl:for-each select="current()/row">
<xsl:call-template name="row"/>
</xsl:for-each>
</tbody>
</xsl:template>
<xsl:template name="thead" match="tgroup/thead">
<xsl:for-each select="current()/row"><thead>
<tr>
<xsl:for-each select="current()/entry">
<xsl:call-template name="headentry"/>
</xsl:for-each>
</tr>
</thead>
</xsl:for-each>
</xsl:template>
<xsl:template name="colspec" match="colspec">
<col class="colnum-{current()/#colnum} colname-{current()/#colname} colwidth-{translate(current()/#colwidth,'%','')}"></col>
</xsl:template>
<xsl:template name="row" match="tbody/row">
<tr>
<xsl:for-each select="current()/entry">
<xsl:call-template name="entry"/>
</xsl:for-each>
</tr>
</xsl:template>
<xsl:template name="entry" match="entry">
<xsl:variable name="count">
<xsl:value-of select="count(preceding-sibling::* | following-sibling::*)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$count < 2">
<xsl:if test="position()=1">
<td>
<div class="para align-center">
<xsl:value-of select="para[position()=1]"/>
</div>
</td>
<td>
<div class="para">
<xsl:value-of select="following-sibling::node()"/>
</div>
</td>
</xsl:if>
</xsl:when>
<xsl:when test="$count > 1">
<td>
<div class="para">
<xsl:apply-templates/>
</div>
</td>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="headentry">
<th>
<xsl:if test="translate(current()/#namest,'col','') != translate(current()/#nameend,'col','')">
<xsl:variable name="colspan">
<xsl:value-of select="translate(current()/#nameend,'col','') - translate(current()/#namest,'col','') + 1"/>
</xsl:variable>
<xsl:attribute name="colspan"><xsl:value-of select="$colspan"></xsl:value-of></xsl:attribute>
</xsl:if>
<div class="para">
<xsl:value-of select="current()/para/text()"/>
</div>
</th>
</xsl:template>
<!-- Table Templates complete -->
<!--List templates -->
<xsl:template name="orderedlist" match="orderedlist">
<ol class="orderedlist">
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template name="orderitem" match="orderlist/item">
<li class="item">
<xsl:apply-templates/>
</li>
</xsl:template>
<xsl:template name="orderitempara" match="item/para">
<xsl:variable name="itemnumber">
<xsl:value-of select="parent::item[1]/#num"/>
</xsl:variable>
<li class="item">
<div class="para">
<span class="item-num">
<xsl:value-of select="parent::item[1]/#num"/>
</span>
<xsl:apply-templates/>
</div>
</li>
</xsl:template>
<!--List templates Complete -->
<!-- Paragraph templates Complete -->
<!-- Footnote Templates-->
<xsl:template match="footnote">
<sup>
<a>
<xsl:attribute name="name"><xsl:text>footnoteref</xsl:text><xsl:number level="any" count="footnote" format="1"/></xsl:attribute>
<xsl:attribute name="href"><xsl:text>#footnote</xsl:text><xsl:number level="any" count="footnote" format="1"/></xsl:attribute>
<xsl:attribute name="class"><xsl:text>tr_ftn</xsl:text><xsl:number level="any" count="footnote" format="1"/></xsl:attribute>
<xsl:number level="any" count="footnote" format="1"/>
</a>
</sup>
</xsl:template>
<xsl:template match="footnote" mode="footnote">
<sup>
<li style="list-style-type:none;indent:0">
<a>
<xsl:attribute name="name"><xsl:text>footnote</xsl:text><xsl:number level="any" count="footnote" format="1"/></xsl:attribute>
<xsl:attribute name="href"><xsl:text>#footnoteref</xsl:text><xsl:number level="any" count="footnote" format="1"/></xsl:attribute>
<xsl:attribute name="class"><xsl:text>tr_ftn</xsl:text><xsl:number level="any" count="footnote" format="1"/></xsl:attribute>
<xsl:number level="any" count="footnote" format="1"/>
</a>
<xsl:text> </xsl:text>
<xsl:apply-templates/>
</li>
</sup>
</xsl:template>
<xsl:template match="footnote/para/uri">
<xsl:variable name="url1">
<xsl:value-of select="translate(#href, '<','')" />
</xsl:variable>
<xsl:variable name="url2">
<xsl:value-of select="translate($url1, '>','')" />
</xsl:variable>
<a href="{$url2}">
<xsl:value-of select="." />
</a>
</xsl:template>
<!-- Footnote Templates Complete -->
<xsl:template match="content-style">
<xsl:choose>
<xsl:when test="#format='smallcaps'">
<xsl:value-of select="translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXZ','abcdefghijklmnopqrstuvwxyz')"/>
</xsl:when>
<xsl:when test="#format='superscript'">
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Namespace ntw-->
<ntw:nums num="01" word="first"/>
<ntw:nums num="02" word="second"/>
<ntw:nums num="03" word="third"/>
<ntw:nums num="04" word="forth"/>
<ntw:nums num="05" word="fifth"/>
<ntw:nums num="06" word="sixth"/>
<ntw:nums num="07" word="seventh"/>
<ntw:nums num="08" word="eighth"/>
<ntw:nums num="09" word="nighth"/>
<ntw:nums num="10" word="tenth"/>
<!-- Namespace ntw ends -->
</xsl:stylesheet>
HTML output of xml.
table style="frame-all width-100">
<colgroup>
<col class="colnum-1 colname-col1 colwidth-18"> //here / is missing in the end
<col class="colnum-2 colname-col2 colwidth-82">//here / is missing in the end
</colgroup>
Thanks
Welcome to Stack Overflow!
Your stylesheet does not specify an output method, but your output is recognizably HTML. So your stylesheet is using the HTML output method, and not the XML output method. In the HTML output method, elements which are always empty (like col) use an SGML-style end-tag, so the output you show is correct.
If you want XML output, add an xsl:output element to the stylesheet (as the first child of xsl:stylesheet) and specify method="xml" on it, thus:
<xsl:output method="xml"/>
(Note that if you are looking to create XHTML output, you will want to put your output elements in the XHTML namespace.)
When I add an output element to the stylesheet, the colgroup element in the output takes the following form, which appears to be what you're looking for:
<colgroup>
<col class="colnum-1 colname-col1 colwidth-18"/>
<col class="colnum-2 colname-col2 colwidth-82"/>
</colgroup>