Distinct Values in XSL1.0 - xslt

I need to convert the following into XSL 1.0 compatibility
<xsl:value-of select="count(distinct-values(/ChangeLog/ChangeSets//WorkItems//WorkItem/_ID))"/>
Have tried
<xsl:value-of select="count(/ChangeLog/ChangeSets//WorkItems//WorkItem/_ID[not(.=following::_ID)])"/>
Here is the sample XML
<ChangeLog>
<ChangeSets>
<ChangeSet>
<ID>31</ID>
<Date>10/30/2012 2:05:59 AM</Date>
<Comment />
<User>XXX</User>
<WorkItems>
<WorkItem>
<_ID>2</_ID>
<_AreaID>1</_AreaID>
<_AuthorizedAs>XXX</_AuthorizedAs>
</WorkItem>
</WorkItems>
</ChangeSet>
<ChangeSet>
<ID>12</ID>
<Date>9/18/2012 7:30:43 AM</Date>
<Comment />
<User>XXX</User>
<WorkItems>
<WorkItem>
<_ID>1</_ID>
<_AreaID>1</_AreaID>
<_AuthorizedAs>XXX</_AuthorizedAs>
</WorkItem>
<WorkItem>
<_ID>2</_ID>
<_AreaID>1</_AreaID>
<_AuthorizedAs>XXX</_AuthorizedAs>
</WorkItem>
</WorkItems>
</ChangeSet>
<ChangeSet>
<ChangeSets>
</ChangeLog>
Here is the XSLT for the same .Mind this is in XSL 2.0 . I need it converted to XSL 1.0 and hence the question . I have updated the stylesheet version alone to point to XSL 1.0
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/ChangeLog">
<html>
<head>
<title>Release Notes Generated from TFS</title>
<style type="text/css">
.HeaderColumnStyle
{
width: 566px;
border: None;
}
.DataColumnStyle
{
border: none; width:auto;
white-space: -moz-pre-wrap; /* Firefox */
white-space: -pre-wrap; /* ancient Opera */
white-space: -o-pre-wrap; /* newer Opera */
white-space: pre-wrap; /* Chrome; W3C standard */
word-wrap: break-word; /* IE */
}
.DescriptionDataColumnStyle
{
border: none;
width: 850px;
white-space: -moz-pre-wrap; /* Firefox */
white-space: -pre-wrap; /* ancient Opera */
white-space: -o-pre-wrap; /* newer Opera */
white-space: pre-wrap; /* Chrome; W3C standard */
word-wrap: break-word; /* IE */
}
.style1
{
border-style: none;
border-color: inherit;
border-width: medium;
width: 60px;
}
</style>
</head>
<body>
<h1 align="center"> TFS Change Log Report</h1>
<table frame="box" border="3" style="width: 100%; border-bottom:#000080 outset;border-left:#000080 outset;border-top:#000080 outset;border-right:#000080 outset; text-align: left; vertical-align: middle;">
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>TFS Server : </xsl:text>
</b>
<xsl:apply-templates select="TFSServer/node()"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Project Name : </xsl:text>
</b>
<xsl:apply-templates select="TFSProjectName/node()"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Branch Location : </xsl:text>
</b>
<xsl:apply-templates select="TFSProjectBranchName/node()"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Report Produced By : </xsl:text>
</b>
<xsl:apply-templates select="ReportProducedBy/node()"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>From Changeset : </xsl:text>
</b>
<xsl:apply-templates select="FromChangeSet/node()"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>To Changeset : </xsl:text>
</b>
<xsl:apply-templates select="ToChangeSet/node()"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Total Number of Changesets : </xsl:text>
</b>
<xsl:value-of select="count(distinct-values(/ChangeLog//ChangeSet/ID))"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Total Number of Work Items : </xsl:text>
</b>
<xsl:value-of select="count(distinct-values(/ChangeLog/ChangeSets//WorkItems//WorkItem/_ID))"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Changesets with associated Work Items : </xsl:text>
</b>
<xsl:value-of select="count(/ChangeLog//ChangeSet[WorkItems/WorkItem/_ID[1] > 0])"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Changesets without associated Work Items : </xsl:text>
</b>
<xsl:value-of select="count(distinct-values(/ChangeLog//ChangeSet/ID)) - count(/ChangeLog//ChangeSet[WorkItems/WorkItem/_ID[1] > 0])"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle" colspan="2">
<b>
<xsl:text>Report Date : </xsl:text>
</b>
<xsl:apply-templates select="ReportDate/node()"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

I have used this several times to select distinct values when doing search engine related products:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:key name="k1" match="_ID" use="."/>
<xsl:template match="/">
<xsl:for-each select="/path/to/iterate[generate-id() = generate-id(key('k1', .)[1])]">
<xsl:value-of select="."/>
<xsl:value-of select="''"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I think it will work for what you are trying to do.

Dave Pawson's XSLT FAQs are an invaluable source. Thanks Mr. Pawson! Here's some code ideas for "duplicates." http://www.dpawson.co.uk/xsl/sect2/N2696.html
The whole collection starts at http://www.dpawson.co.uk/xsl/sect2/sect21.html

Related

How to use <Xsl: apply-templates match="othertemplate" > inside another template <xsl:template match="sometemplate">

Curent output Expected output I am trying to populate a dropdown list using a xsl template inside another xsl template match. I get empty dropdown. How to get the values in the dropdwon from a xsl template ?
I have 2 templates. Template 1 has a list of preferences and template 2 has list of user details. I am trying to populate a table with list of user details. And i need to populate the preference list as once of the column in table as drop down values. I am getting an empty dropdown list. attached image for reference
XML inputs
<USERLIST>
<record num="0"><SERIALNUMER>01</SERIALNUMER><NAME>Rahul</NAME>
<SELECTEDPREFERENCE>Pref2</SELECTEDPREFERENCE></record>
<record num="1"><SERIALNUMER>02</SERIALNUMER><NAME>Khan</NAME>
<SELECTEDPREFERENCE>Pref4</SELECTEDPREFERENCE></record>
<record num="2"><SERIALNUMER>03</SERIALNUMER><NAME>Raju</NAME>
<SELECTEDPREFERENCE>Pref2</SELECTEDPREFERENCE></record>
</USERLIST>
<PREFERENCE_LIST>
<record num="0"><PREFERENCE_ID>pref1</PREFERENCE_ID></record>
<record num="1"><PREFERENCE_ID>pref2</PREFERENCE_ID></record>
<record num="2"><PREFERENCE_ID>pref3</PREFERENCE_ID></record>
<record num="3"><PREFERENCE_ID>pref4</PREFERENCE_ID></record>
<record num="4"><PREFERENCE_ID>pref5</PREFERENCE_ID></record>
</PREFERENCE_LIST>
XSLT
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" />
<xsl:template match="/page">
<div style="margin: 0px; background-color: #ffffff;">
<form action="Useraction" method="post" id="user_search" onsubmit="if (document.getElementById('user_search').action.value=='') return false; else return true;" style="margin: 0px; border: 0px; padding-top: 0px;">
<hr style="color: #003399; width: 100%; height: 3px; border: 0px; margin-top: 0px; margin-bottom: 5px;" />
<table style="width: 100%; text-align: center; border: 0; background-color: #ffffff; margin-bottom: 1px; margin-top: 1px;" cellspacing="0" cellpadding="0">
<tr>
<td class="tableheader3" style="white-space: nowrap; width: 30px;height:30px">
<p align="center">
<input class="tabletext" type="button" value="Alle" onclick="javascript:markAll(document.getElementById('user_search'), 'UserId_')" style="width: 50px;" />
</p>
</td>
<td class="tableheader5" style="width: 30px; text-align: center;">Serial Number</td>
<td class="tableheader5" style="white-space: nowrap; width: 60px;">Name</td>
<td class="tableheader5" style="white-space: nowrap; width: 40px;">Preference</td>
</tr>
<tr>
<td colspan="12">
<hr style="border-top: 1px solid #1A15B7; background: transparent;"> </hr>
</td>
</tr>
<xsl:if test="USERLIST/record">
<xsl:apply-templates select="USERLIST/record" />
<tr>
<td colspan="12">
<img src="images/td_background4.jpg" style="width: 100%; height: 5px; border: 0px;" alt="" />
</td>
</tr>
</xsl:if>
</table>
</form>
</div>
</xsl:template>
<xsl:template match="PREFERENCE_LIST/record">
<xsl:if test="PREFERENCE_ID=/page/parameters/preference">
<option value="{PREFERENCE_ID }" selected="selected ">
<xsl:value-of select="PREFERENCE_ID " />
</option>
</xsl:if>
<xsl:if test="not(PREFERENCE_ID =/page/parameters/preference)">
<option value="{PREFERENCE_ID }">
<xsl:value-of select="PREFERENCE_ID " />
</option>
</xsl:if>
</xsl:template>
<xsl:template match="USERLIST/record">
<tr>
<td class="tabletext" style="white-space: nowrap;">
<xsl:value-of select="SERIALNUMER" />
</td>
<td class="tabletext" style="white-space: nowrap;">
<xsl:value-of select="NAME" />
</td>
<td class="tabletext" style="white-space: nowrap;">
<select class="tabletext" name="preference" style="width:79px;">
<option><xsl:attribute name="value"><xsl:value-of select="SELECTEDPREFERENCE"/>
</xsl:attribute></option>
<option value="" />
<xsl:apply-templates name="PREFERENCE_LIST/record" />
</select>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
As mentioned in comments, name is not a valid attribute on xsl:apply-templates, but assuming you changed it to select the reason it did not select anything is that you are currently in a template matching a record element, and so doing <xsl:apply-templates select="PREFERENCE_LIST/record" /> will look for a child element of record called PREFERENCE_LIST, which does not exist.
You need to navigate back up two levels in the hierarchy to get the PREFERENCE_LIST, so you should actually be writing this:
<xsl:apply-templates select="../../PREFERENCE_LIST/record" />
I am going to hazard a guess that you want the user's SELECTEDPREFERENCE selected as an option in the drop-down list? In this case, you need to pass the user's selected preference to the record template.
Try this two templates instead
<xsl:template match="PREFERENCE_LIST/record">
<xsl:param name="pref" />
<option value="{PREFERENCE_ID}">
<xsl:if test="$pref = PREFERENCE_ID">
<xsl:attribute name="selected">selected</xsl:attribute>
</xsl:if>
<xsl:value-of select="PREFERENCE_ID " />
</option>
</xsl:template>
<xsl:template match="USERLIST/record">
<tr>
<td class="tabletext" style="white-space: nowrap;">
<xsl:value-of select="SERIALNUMER" />
</td>
<td class="tabletext" style="white-space: nowrap;">
<xsl:value-of select="NAME" />
</td>
<td class="tabletext" style="white-space: nowrap;">
<select class="tabletext" name="preference" style="width:79px;">
<option value="" />
<xsl:variable name="selectPref" select="translate(SELECTEDPREFERENCE, 'P', 'p')" />
<xsl:apply-templates select="../../PREFERENCE_LIST/record">
<xsl:with-param name="pref" select="$selectPref" />
</xsl:apply-templates>
</select>
</td>
</tr>
</xsl:template>
Note the use of translate was simply because the check would be case-sensitive, and so Pref2 would not match pref2.
See it in action at http://xsltfiddle.liberty-development.net/ncdD7mS

XSL1.0 Generate Ascending Seq #

I'm using XSL1.0. My editor/debugger is OxygenXML with Saxon (OxygenXML can't debug with MSXML) and it will deployed to work with a 3rd party app that only uses MSXML. This means I can't use a variable containing a nodeset if I want to be able to debug.
The problem could probably be expressed as how to sequentially number output of the following -
<xsl:for-each select="node1">
<xsl:variable name="current_ID" select="ID">
<xsl:for-each select="sub_node1">
<xsl:value-of select="../ID"/>-<xsl:value-of select="Sub_ID"/>
</xsl:for-each>
</xsl:for-each>
understanding that I cannot simply use this in my scenario:
<xsl:for-each select="node1/sub_node1">
<xsl:value-of select="position()"/>
</xsl:for-each>
Below is a manufactured example that shows the problem I'm trying to solve as part of a much larger XSL/XML combo. I basically need to create manufacturing instructions. All nodes in the XML with the exception of products/versions (by qty) are in the correct order and I cannot change it. I need to generate the same set of sequential numbers from 3 different XSL's. My current context will always be shipments/deliveries/delivery_products (i.e. my XSL has to process the nodes in the seq shown). I need to produce a list of products sorted by version qty and their deliveries. Each row should have a sequential no (1-4) in example below
<shipments>
<product>
<name>Product 1</name>
<prod_id>P1</prod_id>
<version>
<version_id>P1_V1</version_id>
<qty>8800</qty>
</version>
<version>
<version_id>P1_V2</version_id>
<qty>1100</qty>
</version>
<version>
<version_id>P1_V3</version_id>
<qty>100</qty>
</version>
</product>
<product>
<name>Product 2</name>
<prod_id>P2</prod_id>
<version>
<version_id>P2_V1</version_id>
<qty>5000</qty>
</version>
<version>
<version_id>P2_V2</version_id>
<qty>5000</qty>
</version>
<version>
<version_id>P2_V3</version_id>
<qty>2000</qty>
</version>
</product>
<deliveries>
<del_id>1</del_id>
<destination>Miami</destination>
<delivery_products>
<version_id>P1_V1</version_id>
<qty>8000</qty>
</delivery_products>
<delivery_products>
<version_id>P2_V1</version_id>
<qty>5000</qty>
</delivery_products>
</deliveries>
<deliveries>
<del_id>2</del_id>
<destination>New York</destination>
<delivery_products>
<version_id>P1_V1</version_id>
<qty>800</qty>
</delivery_products>
<delivery_products>
<version_id>P2_V2</version_id>
<qty>1000</qty>
</delivery_products>
</deliveries>
Expected output is below. Note seq # starts from 1 and counts up to 4
<table>
<thead>
<tr>
<td class="col_head">
Seq
</td>
<td class="col_head">
Version
</td>
<td class="col_head">
Destination
</td>
<td class="col_head">
Qty
</td>
</tr>
</thead>
<tr>
<td colspan="4" class="rev_heading">Product 1</td>
</tr>
<tr>
<td>1</td>
<td>P1_V1</td>
<td>Miami</td>
<td>8000</td>
</tr>
<tr>
<td>2</td>
<td>P1_V1</td>
<td>New York</td>
<td>800</td>
</tr>
<tr>
<td colspan="4" class="rev_heading">Product 2</td>
</tr>
<tr>
<td>3</td>
<td>P2_V1</td>
<td>Miami</td>
<td>5000</td>
</tr>
<tr>
<td>4</td>
<td>P2_V2</td>
<td>New York</td>
<td>5000</td>
</tr>
</table>
Here's my XSL so far (just stuck a position() in for a place holder for the seq #)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
<xsl:template match="shipments">
<html>
<head>
<title>Seq Test</title>
<style type="text/css">
table {border: 1px solid black; border-collapse: collapse;}
td {border: 1px solid black; padding: 1px 5px 1px 5px;}
.col_head {font-weight: 600;}
.rev_heading {color: red; text-align: center; padding-top: 15px;}
</style>
</head>
<body>
<table>
<thead>
<tr>
<!-- SEQ# -->
<td class="col_head">
Seq
</td>
<!-- Imprint/Version -->
<td class="col_head">
Version
</td>
<!-- Ship to -->
<td class="col_head">
Destination
</td>
<!-- Qty -->
<td class="col_head">
Qty
</td>
</tr>
</thead>
<xsl:for-each select="product">
<xsl:sort data-type="number" select="qty"/>
<xsl:for-each select="version">
<xsl:variable name="curr_version" select="version_id"/>
<xsl:if test="position() = 1">
<tr>
<td colspan="4" class="rev_heading">
<xsl:value-of select="../name"/>
</td>
</tr>
</xsl:if>
<xsl:for-each select="../../deliveries/delivery_products[version_id = $curr_version]">
<tr >
<!-- SEQ# -->
<td>
<xsl:value-of select="position()"/>
</td>
<!-- Version -->
<td>
<xsl:value-of select="version_id"/>
</td>
<!-- Ship to -->
<td>
<xsl:value-of select="../destination"/>
</td>
<!-- QTY -->
<td>
<xsl:value-of select="qty"/>
</td>
</tr>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I'm using XSL1.0. My editor/debugger is OxygenXML with Saxon
(OxygenXML can't debug with MSXML) and it will deployed to work with a
3rd party app that only uses MSXML. This means I can't use a variable
containing a nodeset if I want to be able to debug.
You can still use oXygen and the EXSLT node-set() function.
When you are finished, simply change the namespace-uri from "http://exslt.org/common" to "urn:schemas-microsoft-com:xslt"
Here is a short example of this technique. Suppose you are finished debugging the below transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="num[. mod 3 = 0]"/>
</xsl:variable>
<xsl:copy-of select="sum(ext:node-set($vrtfPass1)/*)"/>
</xsl:template>
<xsl:template match="num">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Then you make the change from the EXSLT namespace-uri to the MSXSL namespace uri:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="urn:schemas-microsoft-com:xslt">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="num[. mod 3 = 0]"/>
</xsl:variable>
<xsl:copy-of select="sum(ext:node-set($vrtfPass1)/*)"/>
</xsl:template>
<xsl:template match="num">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
Finally, you run this last transformation with MSXML and it produces exactly the same result as the initial transformation that uses EXSLT:
18

Two-column tables using XSLT

Here is a fraction of the XML data I am processing
<?xml version="1.0" encoding="utf-16"?>
<ScorecardSummary>
<DivisionSummary>
<DivisionName>
<string> SYSTEM</string>
</DivisionName>
<ScorecardSummaryByDivision>
<ScorecardSummaryByKPI>
<Header>
<string>Committed Time of Arrival</string>
<string>Goal</string>
<string>1D</string>
<string>7D</string>
<string>QTD</string>
<string>YTD</string>
<string>YTD Event Cars</string>
</Header>
<Data>
<ScorecardContract>
<TypeName>System</TypeName>
<Goal>68</Goal>
<GoalWarning>64.6</GoalWarning>
<TotalCountYear>1234</TotalCountYear>
<Value1D>79</Value1D>
<Value7D>79.2</Value7D>
<ValueQTD>79.1</ValueQTD>
<ValueYTD>73.3</ValueYTD>
</ScorecardContract>
<ScorecardContract>
<TypeName>AG</TypeName>
<Goal>68</Goal>
<GoalWarning>64.6</GoalWarning>
<TotalCountYear>1111</TotalCountYear>
<Value1D>80.9</Value1D>
<Value7D>78.7</Value7D>
<ValueQTD>78.4</ValueQTD>
<ValueYTD>69.7</ValueYTD>
</ScorecardContract>
This is a small part of the XSL that produces the tables:
<xsl:template match="ScorecardSummary/DivisionSummary/DivisionName">
<h1>
<xsl:value-of select="current()/string"/>
</h1>
</xsl:template>
<xsl:template match="ScorecardSummaryByDivision">
<xsl:apply-templates select="current()/ScorecardSummaryByKPI"/>
</xsl:template>
<xsl:template match="ScorecardSummaryByKPI">
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<xsl:choose>
<xsl:when test="count(preceding-sibling::ScorecardSummaryByKPI) mod 6 < 4">
<td>
<table border="1" cellspacing="0" cellpadding="5">
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Data"/>
</table>
</td>
</xsl:when>
<xsl:otherwise>
<td>
<table border="1" cellspacing="0" cellpadding="5">
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Data"/>
</table>
</td>
</xsl:otherwise>
</xsl:choose>
</tr>
</table>
</xsl:template>
The XSL produces 6 tables repeatedly like this:
1
2
3
4
5
6
1
2
3
4
5
6
But I want to order them like this:
1 4
2 5
3 6
1 4
2 5
3 6
and so on. I tried using this check, but it doesn't work.
count(preceding-sibling::ScorecardSummaryByKPI) mod 6 < 4
Can anyone help?
Explanation
Your table must have two <td> per row (if you want two columns). Your XSLT does generate only one.
Solution is to interate over one half of the list and generate two <td> per iteration.
So first I would define a size of the table. Example:
<xsl:param name="size" select="count(catalog/cd)"/>
Then iterate over only a half of it ($size div 2). The number must be rounded if the input list can contain a non-even number of elements: ceiling($size div 2) (Rounding up to catch last element)
<xsl:for-each select="catalog/cd[ceiling($size div 2) >= position()]">
In each iteration, first render an element itself:
<td><xsl:value-of select="title"/></td>
Then render an appropriate element from the second half of the table (offset is the number defined before: ceiling($size div 2) Half size of the table)
<td><xsl:value-of select="following::cd[ceiling($size div 2)]/title"/></td>
You can wrap element rendering in a separate template to avoid code repeating.
Working example
Check this transformation example with W3C XSL TryIt (http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog):
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:param name="size" select="count(catalog/cd)"/>
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Title</th>
</tr>
<xsl:for-each select="catalog/cd[ceiling($size div 2) >= position()]">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="following::cd[ceiling($size div 2)]/title"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
It splits CD-catalog (given in example link above) in two columns.
Perhaps something like this is what you are looking for:
(This only shows the idea, you have to adapt it to your input.)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/" >
<xsl:apply-templates select="//t[count(preceding-sibling::t) < 3]" mode="tables" />
</xsl:template>
<xsl:template match="t" >
{<xsl:value-of select="text()"/>}
</xsl:template>
<xsl:template match="t" mode="tables">
<table border="1">
<tr>
<td>
<table border="1" >
<xsl:apply-templates select="." />
</table>
</td>
<td>
<table border="1">
<xsl:apply-templates select="following-sibling::t[count(preceding-sibling::t) = count(current()/preceding-sibling::t) +3]" />
</table>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
With this short test input xml:
<?xml version="1.0" encoding="utf-8"?>
<xml>
<tables>
<t>1</t>
<t>2</t>
<t>3</t>
<t>4</t>
<t>5</t>
<t>6</t>
</tables>
</xml>
It will generate this output:
<?xml version="1.0"?>
<table xmlns="http://www.w3.org/1999/xhtml" border="1">
<tr>
<td>
<table border="1">
{1}
</table>
</td>
<td>
<table border="1">
{4}
</table>
</td>
</tr>
</table><table xmlns="http://www.w3.org/1999/xhtml" border="1">
<tr>
<td>
<table border="1">
{2}
</table>
</td>
<td>
<table border="1">
{5}
</table>
</td>
</tr>
</table><table xmlns="http://www.w3.org/1999/xhtml" border="1">
<tr>
<td>
<table border="1">
{3}
</table>
</td>
<td>
<table border="1">
{6}
</table>
</td>
</tr>
</table>

Extracting the attributes when the occurance of them is not particular

I am working on the extracting the values background color and width attributes from "td" of a table.
There are several ways of occurance of Back ground color and width. T
I have following set of valid elements.
1.<td style="BACKGROUND-COLOR: yellow; WIDTH: 52%"></td>
(combination of BACKGROUND-COLOR and Width in one order)
2.<td style="WIDTH: 52%;BACKGROUND-COLOR: green"></td>
(combination of BACKGROUND-COLOR and Width in another order)
3.<td style="WIDTH:52%;BACKGROUND-COLOR: green"></td>
(Spaces could vary from ":" and value)
4.<td style="BACKGROUND-COLOR: gray"></td>
(only BACKGROUND-COLOR in style)
5.<td style="BACKGROUND-COLOR: Gray"></td>
(Value of BACKGROUND-COLOR can be case sensitive)
6.<td style="BACKGROUND-COLOR: #ffff00"></td>
(value of BACKGROUND-COLOR can be hexadecimal also)
7.<td bgcolor="#008000" style="WIDTH: 54%">
(BACKGROUND-COLOR can occur as bgcolr also(hexadecimal code)
8.<td bgcolor="yellow">
(BACKGROUND-COLOR can occur as bgcolr also(string))
List of valid colors and their codes:(all the values are case sensitive)
Yellow:#ffff00
Gray:#808080
Green:#008000
Output:
1.<bgclr>Yellow</bgclr>
<colwidth>52</colwidth>
2.<bgclr>Green</bgclr>
<colwidth>52</colwidth>
3. <bgclr>Green</bgclr>
<colwidth>52</colwidth>
4.<bgclr>Gray</bgclr>
5.<bgclr>Gray</bgclr>
6.<bgclr>Yellow</bgclr>
7.<bgclr>Green</bgclr>
<colwidth>54</colwidth>
8.<bgclr>Yellow</bgclr>
I have tried my level best to solve this, and it looks complicated for me.
I am also providing the valid xml file.
<tr>
<td style="BACKGROUND-COLOR: yellow; WIDTH: 52%"></td>
<td style="WIDTH: 52%;BACKGROUND-COLOR: green"></td>
<td style="WIDTH:52%;BACKGROUND-COLOR: green"></td>
<td style="BACKGROUND-COLOR: gray"></td>
<td style="BACKGROUND-COLOR: Gray"></td>
<td style="BACKGROUND-COLOR: #ffff00"></td>
<td bgcolor="#008000" style="WIDTH: 54%"></td>
<td bgcolor="yellow"></td>
</tr>
Can any one help on this.
Thanks.
We can leverage the power of xsl:apply-templates and predicates to avoid a lot of painful xsl:choose statements. A template approach will also give us a more modular solution.
This XSLT 1.0 style-sheet ...
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:template match="/">
<tr>
<xsl:apply-templates select="*/td"/>
</tr>
</xsl:template>
<xsl:template match="td">
<td>
<xsl:apply-templates
select="self::*[contains(#style,'BACKGROUND-COLOR:') or #bgcolor]"
mode="bg-colour"/>
<xsl:apply-templates
select="self::*[contains(#style,'WIDTH:')]"
mode="width"/>
</td>
</xsl:template>
<xsl:template match="td[#bgcolor]" mode="bg-colour">
<xsl:call-template name="render-bg-colour">
<xsl:with-param name="raw-colour" select="#bgcolor" />
</xsl:call-template>
</xsl:template>
<xsl:template match="td" mode="bg-colour">
<xsl:call-template name="render-bg-colour">
<xsl:with-param name="raw-colour" select="
substring-before( substring-after(concat(#style,';'),'BACKGROUND-COLOR:'), ';')" />
</xsl:call-template>
</xsl:template>
<xsl:variable name="palette">
<colours>
<colour>
<code>#ffff00</code>
<name>Yellow</name>
</colour>
<colour>
<code>#808080</code>
<name>Gray</name>
</colour>
<colour>
<code>#008000</code>
<name>Green</name>
</colour>
</colours>
</xsl:variable>
<xsl:template name="render-bg-colour">
<xsl:param name="raw-colour" />
<xsl:variable name="trim-colour" select="normalize-space( $raw-colour)" />
<xsl:variable name="canon-colour" select="
document('')//xsl:variable[#name='palette']/colours/colour[
(translate($trim-colour, $uppercase, $lowercase) =
translate(name , $uppercase, $lowercase) ) or
($raw-colour = code)
]/name/text()"/>
<bgclr>
<xsl:value-of select="$canon-colour" />
</bgclr>
</xsl:template>
<xsl:template match="td" mode="width">
<colwidth>
<xsl:value-of select="
normalize-space( substring-before( substring-after(#style,'WIDTH:'), '%'))" />
</colwidth>
</xsl:template>
</xsl:stylesheet>
... when applied to this input document ...
<tr>
<td style="BACKGROUND-COLOR: yellow; WIDTH: 52%"></td>
<td style="WIDTH: 52%;BACKGROUND-COLOR: green"></td>
<td style="WIDTH:52%;BACKGROUND-COLOR: green"></td>
<td style="BACKGROUND-COLOR: gray"></td>
<td style="BACKGROUND-COLOR: Gray"></td>
<td style="BACKGROUND-COLOR: #ffff00"></td>
<td bgcolor="#008000" style="WIDTH: 54%"></td>
<td bgcolor="yellow"></td>
</tr>
... will yield this output document...
<tr>
<td>
<bgclr>Yellow</bgclr>
<colwidth>52</colwidth>
</td>
<td>
<bgclr>Green</bgclr>
<colwidth>52</colwidth>
</td>
<td>
<bgclr>Green</bgclr>
<colwidth>52</colwidth>
</td>
<td>
<bgclr>Gray</bgclr>
</td>
<td>
<bgclr>Gray</bgclr>
</td>
<td>
<bgclr>Yellow</bgclr>
</td>
<td>
<bgclr>Green</bgclr>
<colwidth>54</colwidth>
</td>
<td>
<bgclr>Yellow</bgclr>
</td>
</tr>

How to transform an XML with default namespace?

I need some to help oon generating the XSL file for my XML Data.
Here is my XML Data
<?xml-stylesheet href="C:\Style.xsl" type="text/xsl" ?>
<xml>
<ApproverRoles OperationType="RemovedUser" xmlns="http://tempuri.org/">
<UserName>Bhupathiraju, Venkata</UserName><UserRole>IT Owner</UserRole><RoleDescription>Role Owner
</RoleDescription><UserRoleID>138</UserRoleID></ApproverRoles>
<ApproverRoles OperationType="RemovedUser" xmlns="http://tempuri.org/">
<UserName>Bhupathiraju, Venkata</UserName><UserRole>Business Owner</UserRole>
<RoleDescription>Role Owner</RoleDescription><UserRoleID>136</UserRoleID></ApproverRoles>
<ApproverRoles OperationType="RemovedUser" xmlns="http://tempuri.org/"><UserName>Amperayeni, Kiran K</UserName>
<UserRole>IT Owner</UserRole><RoleDescription>asdasdasd</RoleDescription><UserRoleID>97</UserRoleID>
</ApproverRoles>
<ApproverRoles OperationType="RemovedUser" xmlns="http://tempuri.org/"><UserName>Amperayeni, Kiran K</UserName>
<UserRole>IT Owner</UserRole><RoleDescription>i</RoleDescription><UserRoleID>135</UserRoleID></ApproverRoles>
</xml>
My XSL file is below
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match ="/" >
<html>
<head>
<title>User Management</title>
</head>
<body>
<table width="600" border="1" style='font-family:Calibri;font-size:10pt;background-color:#FFFFFF;border-color:#ccccff'>
<tr bgcolor = "#ccccff" style='font-weight:bold;'>
<td colspan="3">Proposed Users :</td>
</tr>
<tr bgcolor = "#cccccc" style='font-weight:bold;'>
<td>User Name</td>
<td>Role</td>
<td>Role Qualifier</td>
</tr>
<xsl:for-each select="//ns1:ApproverRoles" >
<tr>
<td>
<xsl:value-of select="UserName" />
</td>
<td>
<xsl:value-of select="UserRole" />
</td>
<td>
<xsl:value-of select="RoleDescription" />
</td>
</tr>
</xsl:for-each>
<tr bgcolor = "#ccccff" style='font-weight:bold;'>
<td colspan="3">Removed Users :</td>
</tr>
<tr bgcolor = "#cccccc" style='font-weight:bold;'>
<td>User Name</td>
<td>Role</td>
<td>Role Qualifier</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet >
You are not correctly dealing with the default namespace present in the input document. If you do not associate a prefix to the corresponding namespace uri, the XSLT processor will search for elements in no namespace. Actually, the elements in your input document, are all in the namespace http://tempuri.org/.
So, you need first to declare the namespace prefix in the transform:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://tempuri.org/">
Then, you have to use the prefix accordingly. For instance:
<xsl:for-each select="//ns1:ApproverRoles" >
<tr>
<td>
<xsl:value-of select="ns1:UserName" />
</td>
<td>
<xsl:value-of select="ns1:UserRole" />
</td>
<td>
<xsl:value-of select="ns1:RoleDescription" />
</td>
</tr>
</xsl:for-each>