I am working on improving on some stylesheets that I have inherited and converting them from using <xsl:for-each> to <xsl:apply-templates>. A very simplified version of one of the XML files I will be working with is:
<Root>
<Row ID="123" Region="AMS">
<First>Graham</First>
<Last>Smith</Last>
<Sales>12345.85</Sales>
<Team>Team A</Team>
</Row>
<Row id="321">
<First>John</First>
<Last>Brown</Last>
<Sales>18765.85</Sales>
<Team>Team C</Team>
</Row>
<Row id="456" Region="EMEA">
<First>Anne</First>
<Last>Jones</Last>
<Sales>34567.85</Sales>
<Team>Team B</Team>
</Row>
</Root>
The new stylesheet I have is:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:variable name="RowCount" select="count(/*/*)"/>
<xsl:template match="/#* | node()">
<style>
body * {font-family:Arial;font-size:11pt}
table {border-collapse:collapse}
td {border-bottom:1px solid #D8D8D8;padding:7px}
tr.row1 {background:#F9F9F9;}
td.tdHeader {border-bottom:2px solid #DDD;font-weight:700}
</style>
<table>
<thead>
<tr>
<xsl:apply-templates select="*[1]/#*" mode="headerAttributes" />
<xsl:apply-templates select="*[1]/*" mode="headerFields"/>
</tr>
</thead>
<tbody>
<xsl:apply-templates select="*"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="/*/*/#*" mode="headerAttributes">
<td class="tdHeader">
<xsl:value-of select="name()" />
</td>
</xsl:template>
<xsl:template match="/*/*/*" mode="headerFields">
<td class="tdHeader">
<xsl:value-of select="name()" />
</td>
</xsl:template>
<xsl:template match="/*/*">
<tr class="row{position() mod 2}">
<xsl:apply-templates select="#*" mode="attributes"/>
<xsl:apply-templates select="*" mode="fields"/>
</tr>
</xsl:template>
<xsl:template match="/*/*/#*" mode="attributes">
<td>
<xsl:value-of select="." />
</td>
</xsl:template>
<xsl:template match="/*/*/*" mode="fields">
<td>
<xsl:value-of select="." />
</td>
</xsl:template>
</xsl:stylesheet>
but, due to the second node in the XML is missing the <Region> attribute, the cells on the result are mis-aligned, with first name now in the Region column, Last name in First name column and so on. This also happens if there is a missing child node on the Row node. for example, no team element
I have tried to test for a missing node, before calling the apply-template, and within the last two templates, but to no avail.
Any ideas? What am I missing here? I am only just starting to get my head around using apply-templates, but other methods of writing stylesheets I am fine with.
Provisionally, this works for your input:
<xsl:template match="Row">
<tr class="row{position() mod 2}">
<td><xsl:apply-templates select="#ID|#id" mode="attr2"/></td>
<td><xsl:apply-templates select="#Region" mode="attr2"/></td>
<xsl:apply-templates select="*" mode="fields"/>
</tr>
</xsl:template>
<xsl:template match="#ID|#id|#Region" mode="attr2">
<b><xsl:value-of select="." /></b>
</xsl:template>
-- and you may remove the catch-all for mode="attributes".
This forces the inclusion of a <td>..</td> pair even if there is no id|ID or Region attribute.
Saxon 8.8 reports an error:
The attribute axis starting at a document-node() node will never select anything
because of your
<xsl:template match="/#* | node()">
Changing it to <xsl:template match="node()">, or, preferably, to <xsl:template match="Root"> will fix this. As I said in my comment, try to use * as little as possible. References to Root and Row can be changed to exact matches.
Related
I need to generate an HTML table using the attribute names as header and the attribute values as the data. However the elements that need to be looped do not have all the attributes present all the time. In such a scenario a blank <td> needs to be added to the table for all such non-available attribute values.
Below is a sample XML (a scaled down version with dummy values).
<root>
<oelement attr="abc">
<ielement attr="101">
<child attr1="a" attr2="b" attr3="c" attr4="d" attr5="e" />
<child attr1="e" attr2="f" attr3="g" attr4="h" attr5="i" />
</ielement>
<ielement attr="102">
<child attr1="x" attr3="y" attr5="w" />
<child attr1="j" attr3="k" attr5="l" />
</ielement>
</oelement>
<oelement attr="pqr">
<ielement attr="101">
<child attr1="g" attr2="j" attr3="t" attr4="y" attr5="r" />
<child attr1="d" attr2="q" attr3="a" attr4="c" attr5="b" />
</ielement>
<ielement attr="102">
<child attr1="t" attr3="y" attr5="u" />
<child attr1="i" attr3="o" attr5="p" />
</ielement>
</oelement>
<oelement attr="xyz">
<ielement attr="101">
<child attr1="h" attr2="o" attr3="u" attr4="z" attr5="x" />
</ielement>
<ielement attr="103">
<child attr1="q" attr3="w" attr5="e" />
</ielement>
</oelement>
</root>
Output HTML
I have tried to put together the following XSLT, however it does not match the attribute names in column header when loading the data in the appropriate column of the table.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:strip-space elements="*" />
<xsl:template match="root">
<html>
<body>
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<th>oelement</th>
<th>ielement</th>
<xsl:for-each select="//oelement[1]/ielement[1]/child[1]/#*">
<th><xsl:value-of select="local-name()" /></th>
</xsl:for-each>
</tr>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/#attr" /></td>
<td><xsl:value-of select="ancestor::ielement/#attr" /></td>
<xsl:for-each select="#*">
<td><xsl:value-of select="." /></td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
Need help in doing the column matching when adding value and inserting blank value for non-matching column. I am stuck with XSLT 1.0 and cannot upgrade to XSLT 2.0 for finding the distinct-values() of the attribute names.
There is no nice solution to do this in XLST 1.0.
You can use a key to achieve the desired effect, though:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:strip-space elements="*" />
<xsl:key name="child-by-attr-name" match="child/#*" use="name(.)"/>
<xsl:template match="root">
<html>
<body>
<table border="1" cellspacing="0" cellpadding="5">
<!-- skipped for simplicity -->
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/#attr" /></td>
<td><xsl:value-of select="ancestor::ielement/#attr" /></td>
<xsl:variable name="current-child" select="."/>
<xsl:for-each select="(//child/#*)[generate-id(.) = generate-id(key('child-by-attr-name',name(.))[1])]">
<td>
<xsl:value-of select="$current-child/#*[name(.) = name(current())]" />
</td>
</xsl:for-each>
</tr>
</xsl:template>
The "trick" here is that the key will index all attributes (in every child element) by it's name. When you query the key by an attribute name, you get all occurrences of attributes with that name in document order. You can then use generate-id() to make sure you only get the first one of each name.
The key to success is a proper way to loop through attribure names.
To do this, I used:
A key (named attrib), matching child attributes and saving their names (name()).
Two loops, one creating the header, and another printing attribute values.
To cope with changes in the context object in the template matching child,
and for readability, I used 2 variables:
curr - the current child element.
nn - the name of the current attribute.
So the whole script can look like below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:strip-space elements="*" />
<xsl:key name="attrib" match="child/#*" use="name()"/>
<xsl:template match="root">
<html>
<body>
<table>
<tr>
<th>oelement</th>
<th>ielement</th>
<xsl:for-each select="//child/#*[generate-id()=generate-id(key('attrib', name())[1])]">
<th><xsl:value-of select="name()"/></th>
</xsl:for-each>
</tr>
<xsl:apply-templates select="//child"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/#attr" /></td>
<td><xsl:value-of select="ancestor::ielement/#attr" /></td>
<xsl:variable name="curr" select="."/>
<xsl:for-each select="//child/#*[generate-id()=generate-id(key('attrib', name())[1])]">
<td>
<xsl:variable name="nn" select="name()"/>
<xsl:value-of select="$curr/#*[name()=$nn]"/>
</td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
While trying to implement the pattern "Two Step View" as described by Martin Fowler, I had some problems getting the alternate-row colouring of the HTML table to work. This uses the XSLT position() function. You can see the XSLT template for table/row below. However, in the output, the bgcolor attribute of the tr element is always "linen", indicating that the value of position() is not changing as we iterate over table/row elements. Why would this be?
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="screen">
<html>
<body bgcolor="white">
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="title">
<h1>
<xsl:apply-templates/>
</h1>
</xsl:template>
<xsl:template match="field">
<p><b><xsl:value-of select="#label"/>: </b><xsl:apply-templates/></p>
</xsl:template>
<xsl:template match="table">
<table><xsl:apply-templates/></table>
</xsl:template>
<xsl:template match="table/row">
<xsl:variable name="bgcolor">
<xsl:choose>
<xsl:when test="(position() mod 2) = 0">linen</xsl:when>
<xsl:otherwise>white</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr bgcolor="{$bgcolor}"><xsl:apply-templates/></tr>
</xsl:template>
<xsl:template match="table/row/cell">
<td><xsl:apply-templates/></td>
</xsl:template>
</xsl:stylesheet>
Input XML:
<?xml version="1.0"?>
<screen>
<title>Dissociation</title>
<field label="Artist">Dillinger Escape Plan</field>
<table>
<row>
<cell>Limerent Death</cell>
<cell>4:06</cell>
</row>
<row>
<cell>Symptom Of Terminal Illness</cell>
<cell>4:03</cell>
</row>
<row>
<cell>Wanting Not So Much To As To</cell>
<cell>5:23</cell>
</row>
</table>
</screen>
Output HTML:
<html><body bgcolor="white">
<h1>Dissociation</h1>
<p><b>Artist: </b>Dillinger Escape Plan</p>
<table>
<tr bgcolor="linen">
<td>Limerent Death</td>
<td>4:06</td>
</tr>
<tr bgcolor="linen">
<td>Symptom Of Terminal Illness</td>
<td>4:03</td>
</tr>
<tr bgcolor="linen">
<td>Wanting Not So Much To As To</td>
<td>5:23</td>
</tr>
</table>
</body></html>
Change <table><xsl:apply-templates/></table> to <table><xsl:apply-templates select="row"/></table> or use <xsl:strip-space elements="*"/> or at least <xsl:strip-space elements="table"/>. Currently you are processing all child nodes, including white space text nodes, that way your attempt using position() fails.
I am a newcomer to XML/XSLT and, so far, in searching SO and the W3SChools sites for clever stuff on Meunchian grouping I haven't been able to grasp an effective solution to my challenge.
Essentially I have one large XML file exported from a database (which means I can't directly edit the XML) which contains invoicing information.
I want to utilise XSLT (1.0) to apply a transformation of the XML into HTML (I am using Saxon) so that each invoice is displayed as a table. However, in the XML, there are many product lines which relate to the same invoice (denoted by the <invoiceNum> element as the identifier).
I don't want to have to create & display a new table for each product line which is part of the same invoice. In my current XSL file, I am trying to create a table for the first instance of a duplicated <invoiceNum> element, and then add only the unique elements from successive product lines (<ProductID>, <ProductName>, <ProductDescription> etc.) and leave out the duplicated shipping information.
In the XML code snippet, you can see the layout of the XML. In the XSL snippet, I hope you can see how I am trying to construct the table. For each successive <invoices_snet> instance in the XML file, I want to add just the product information to the table through the transformation. Using for-each on the <invoices_snet> element simply creates a new table each time.
Should I use conditional logic here, compare the equality of the value of the <invoices_snet> element, use templates?
Your help is much appreciated!
<database>
<invoices>
<invoices_snet>
<invoiceNum NAME="invoiceNum" TYPE="SMALLINT">368</invoiceNum>
<ProductID NAME="ProductID" TYPE="VARCHAR">SS106</ProductID>
<ProductName NAME="ProductName" TYPE="VARCHAR">Senna Sunglasses</ProductName>
<ProductDescription NAME="ProductDescription" TYPE="VARCHAR">Lively sunglasses</ProductDescription>
<Quantity NAME="Quantity" TYPE="SMALLINT">34</Quantity>
<UnitPrice NAME="UnitPrice" TYPE="CURRENCY">40.0000</UnitPrice>
<ExtendedPrice NAME="ExtendedPrice" TYPE="CURRENCY">1360.0000</ExtendedPrice>
<contactName NAME="contactName" TYPE="VARCHAR">Jeff</contactName>
<shippingStreet NAME="shippingStreet" TYPE="VARCHAR">11 Acacia Avenue</shippingStreet>
<shippingCity NAME="shippingCity" TYPE="VARCHAR">Huddersfield</shippingCity>
<shippingCounty NAME="shippingCounty" TYPE="VARCHAR">Yorkshire</shippingCounty>
<shippingPostcode NAME="shippingPostcode" TYPE="VARCHAR">YO12 8LA</shippingPostcode>
<saleDate NAME="salesDate" TYPE="DATETIME">30. Mar. 16</saleDate>
</invoices_snet>
<invoices_snet>
<invoiceNum NAME="invoiceNum" TYPE="SMALLINT">368</invoiceNum>
<ProductID NAME="ProductID" TYPE="VARCHAR">SS368</ProductID>
<ProductName NAME="ProductName" TYPE="VARCHAR">Senna T-shirts</ProductName>
<ProductDescription NAME="ProductDescription" TYPE="VARCHAR">T-shirts of beige colour with cream piping</ProductDescription>
<Quantity NAME="Quantity" TYPE="SMALLINT">20</Quantity>
<UnitPrice NAME="UnitPrice" TYPE="CURRENCY">15.00</UnitPrice>
<ExtendedPrice NAME="ExtendedPrice" TYPE="CURRENCY">300.00</ExtendedPrice>
<contactName NAME="contactName" TYPE="VARCHAR">Jeff</contactName>
<shippingStreet NAME="shippingStreet" TYPE="VARCHAR">11 Acacia Avenue</shippingStreet>
<shippingCity NAME="shippingCity" TYPE="VARCHAR">Huddersfield</shippingCity>
<shippingCounty NAME="shippingCounty" TYPE="VARCHAR">Yorkshire</shippingCounty>
<shippingPostcode NAME="shippingPostcode" TYPE="VARCHAR">YO12 8LA</shippingPostcode>
<saleDate NAME="salesDate" TYPE="DATETIME">30. Mar. 16</saleDate>
</invoices_snet>
</invoices>
</database>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/database">
<html>
<head>
<title>Invoice</title>
<link rel="stylesheet" href="invoicingCSS.css"/>
</head>
<body>
<h1>Invoice</h1>
<br></br>
<xsl:for-each select="invoices/invoices_snet">
<h2>Order for Invoice Number: <xsl:value-of select="invoiceNum"/> </h2>
<table>
<tr>
<th>Product ID</th>
<th>Product Name</th>
<th>Product Description</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Extended Price</th>
<th>Contact Name</th>
<th>Shipping Address</th>
<th>Sales Date</th>
</tr>
<tr>
<td>
<xsl:value-of select="ProductID"/>
</td>
<td>
<xsl:value-of select="ProductName"/>
</td>
<td>
<xsl:value-of select="ProductDescription"/>
</td>
<td>
<xsl:value-of select="Quantity"/>
</td>
<td>
<xsl:value-of select="UnitPrice"/>
</td>
<td>
<xsl:value-of select="ExtendedPrice"/>
</td>
<td>
<xsl:value-of select="contactName"/>
</td>
<td>
<xsl:value-of select="concat(
shippingStreet,' ',
shippingCity,', ',
shippingCounty,', ',
shippingPostcode)"
/>
</td>
<td>
<xsl:value-of select="saleDate"/>
</td>
</tr>
</table>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Grouping is much easier to do in XSLT 2.0, with its built-in xsl:for-each-group instruction.
Try this as your starting point:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="theader">
<tr>
<th>ProductID</th>
<th>Quantity</th>
<!-- add more as required -->
</tr>
</xsl:variable>
<xsl:template match="/database">
<html>
<xsl:for-each-group select="invoices/invoices_snet" group-by="invoiceNum">
<table border="1">
<xsl:copy-of select="$theader"/>
<xsl:apply-templates select="current-group()"/>
</table>
</xsl:for-each-group>
</html>
</xsl:template>
<xsl:template match="invoices_snet">
<tr>
<td>
<xsl:value-of select="ProductID"/>
</td>
<td>
<xsl:value-of select="Quantity"/>
</td>
<!-- add more as required -->
</tr>
</xsl:template>
</xsl:stylesheet>
<xsl:template match="invoices_snet">
<tr>
<td>
<xsl:value-of select="ProductID"/>
</td>
<td>
<xsl:value-of select="Quantity"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
First do you self a favor and have a look for xslt grouping for XSLT 1.0 this will be muenchian grouping e.g. this
And here the basic structure for a adaption to your xml:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:key name="kinvoices_snet" match="invoices_snet" use="invoiceNum"/>
<xsl:template match="invoices">
<xsl:for-each select="invoices_snet [
count ( key('kinvoices_snet',./invoiceNum)[1] | . ) = 1 ]">
<xsl:variable name="this" select="." />
<g>
<gh>
<xsl:value-of select="invoiceNum" />
</gh>
<!-- group stuff -->
<xsl:for-each select=" key('kinvoices_snet',$this/invoiceNum)">
<!-- group member stuff -->
<gm>
<xsl:value-of select="ProductID" />
</gm>
</xsl:for-each>
</g>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This is my test XML in this XML i have child element: INSTRUMENT and sub child element: INSTRUMENT/issuer and could be so on ...
5002199
10001
686184SE3
<INSTRUMENT>
<type>FI</type>
<issuer>
<FICode>123456</FICode>
<name>Test</name>
<city>SF</city>
<state>CA</state>
</issuer>
<issueDate>2011-06-22-05:00</issueDate>
<maturityDate>2016-06-22-05:00</maturityDate>
<firstCouponDate>2011-07-22-05:00</firstCouponDate>
<lastCouponDate>2016-05-22-05:00</lastCouponDate>
<couponRate>2.0</couponRate>
<paymentFrequency>12</paymentFrequency>
<callSchedule>
<notice>15</notice>
<timing>0</timing>
<call id="1">
<startDate>2011-12-22-05:00</startDate>
<type>2</type>
<freq>M</freq>
</call>
</callSchedule>
</INSTRUMENT>
<Commision>7.0</Commision>
<price>100.0</price>
I want to display this data in HTML tabular form, and run time the xml element could be any thing, so i can't hard code the element or subelement name
i was trying follwinf XSL
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="*">
<table border="1" width="1000">
<tr>
<td class="section_head">Key</td>
<td class="section_head">Value</td>
</tr>
<xsl:for-each select="*" >
<tr>
<td>
<xsl:value-of select="name(.)" />
</td>
<td>
<xsl:value-of select="." />
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
the HTM Child INSTRUMENT and issuer and callSchedule is comming in tabular form
is there any way i can iterate XSL recursivley to create HTML child table for XML child elements ?
Key
Value
ID
5002199
Code
10001
cusip
686184SE3
INSTRUMENT
FI 123456 Test SF CA 2011-06-22-05:00 2016-06-22-05:00 2011-07-22-05:00 2016-05-22-05:00 2.0 12 15 0 2011-12-22-05:00 100.0 2 M 2012-01-22-05:00 100.0 2012-02-22-05:00 100
Commision
7.0
price
100.0
alloc
100
I am not exactly sure what you are after here. It looks like you want to convert the nodes into name/value pairs and nest the html tables according to the XML hierarchy. Here is a bit of recursion that addresses the problem of not knowing the node names at runtime. Hopefully it well help you get started with this. If it is not what you are looking for you can use it to clarify your question:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<table>
<xsl:apply-templates/>
</table>
</html>
</xsl:template>
<xsl:template match="*[count(*) = 0]">
<tr>
<td>
<xsl:value-of select="name(.)" />
</td>
<td>
<xsl:value-of select="." />
</td>
</tr>
</xsl:template>
<xsl:template match="*[count(*) > 0]">
<tr>
<td>
<xsl:value-of select="name(.)" />
</td>
<td>
<table>
<xsl:apply-templates/>
</table>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
In brief, my problem is that I want to loop through the contents of one child among identical children, but when I try to do so, it loops through the contents of all the children, giving me as many duplicates of the data as there are children.
Example code:
Input.xml
<?xml version="1.0"?>
<base>
<item name="item1">
<foo>
<type1>1</type1>
<type2>2</type2>
</foo>
<bar>
<type3>3</type3>
</bar>
</item>
<item name="item2">
<foo>
<type1>4</type1>
<type2>5</type2>
</foo>
<bar>
<type3></type3>
<!-- NOTE! This value is missing. Therefore we must put a blank value in the table-->
</bar>
</item>
<item name="item3">
<foo>
<type1>7</type1>
<type2></type2>
<!-- NOTE! This value is missing. Therefore we must put a blank value in the table-->
</foo>
<bar>
<type3>9</type3>
</bar>
</item>
</base>
tableMaker.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cyg="http://CygNetSCADA.com/Schemas/CygNetEnterpriseObjects730">
<xsl:output method="html" encoding="UTF-8" />
<xsl:template match="/">
<html>
<body>
<table border="1">
<tr>
<th>Name</th>
<xsl:for-each select="base/item/*/*">
<th>
<xsl:value-of select="local-name()"/>
</th>
</xsl:for-each>
</tr>
<xsl:for-each select="base/item">
<tr>
<th>
<xsl:value-of select="#name"/>
</th>
<xsl:for-each select="foo/*">
<xsl:choose>
<xsl:when test=".[node()]">
<td>
<xsl:value-of select="." />
</td>
</xsl:when>
<xsl:otherwise>
<td />
<!-- This is for that empty value in item3 -->
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:for-each select="bar/*">
<xsl:choose>
<xsl:when test=".[node()]">
<td>
<xsl:value-of select="." />
</td>
</xsl:when>
<xsl:otherwise>
<td />
<!-- This is for that empty value in item2 -->
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
What happens in this example is that the generated HTML has 10 columns - one for "name", and "type1", "type2", "type3" three times (three times for the three elements; if there were 4 elements in my input there would be 3 more columns). I only want "type1", "type2", and "type3" to display once in the column. How might I go about doing this?
All help is appreciated and thanks in advance!
You're using XSL 2.0 so presumably you have access to the xsl:for-each-group element. It's what you need in this situation:
<tr>
<th>Name</th>
<xsl:for-each-group select="base/item/*/*" group-by="local-name()">
<th>
<xsl:value-of select="current-grouping-key()"/>
</th>
</xsl:for-each>
</tr>