Reputation: 5481
XML
<Action>
<Step>
<Obj>UC_A</Obj>
<Step>
<Obj>abc</Obj>
<Step>
<Obj>bcd</Obj>
</Step>
</Step>
</Step>
<Step>
<Obj>cde</Obj>
</Step>
<Step>
<Obj>UC_B</Obj>
</Step>
<Step>
<Obj>def</Obj>
<Step>
<Obj>efg</Obj>
</Step>
</Step>
<Step>
<Obj>UC_C</Obj>
<Step>
<Obj>pqr</Obj>
</Step>
<Step>
<Obj>xyz</Obj>
<Step>
<Obj>uvw</Obj>
</Step>
</Step>
</Step>
</Action>
My XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" version="5.0" doctype-system="about:legacy-compat" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="group" match="Step[not(starts-with(Obj, 'UC_'))]" use="generate-id(ancestor::Step[starts-with(Obj, 'UC_')][1])"/>
<xsl:template match="Action">
<html>
<head>
<style type="text/css">
table { table-layout:auto; border-collapse:collapse; font-family:Arial; }
td { vertical-align:top; border:1px solid Silver; padding:0pt; font-size:9pt; padding-right:3px; padding-left:3px; }
</style>
</head>
<body>
<table>
<xsl:apply-templates select="Step[starts-with(Obj, 'UC_')]"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Step[starts-with(Obj, 'UC_')]">
<xsl:variable name="group" select="key('group', generate-id())"/>
<tr>
<td rowspan="{1 + count($group)}">
<xsl:number count="Step[starts-with(Obj, 'UC_')]"/>
</td>
<td>
<xsl:value-of select="Obj"/>
</td>
</tr>
<xsl:apply-templates select="$group" />
</xsl:template>
<xsl:template match="Step[not(starts-with(Obj, 'UC_'))]">
<tr>
<td>
<xsl:value-of select="Obj"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
Problems
Expected HTML
Actual HTML
My current XSL isn't considering the nodes which are at the same level as UC_* node (like cde) or which are in 2nd level. I want to put a rowspan for a group of UC_ nodes.
I do not want to use grouping as it is not very efficient (my actual xml is huge)
Upvotes: 0
Views: 165
Reputation: 70648
At the moment your key is only looking at ancestor nodes, but Step
nodes don't have "UC_" value as an ancestor. In such cases, you need to look for preceding elements, rather than the ancestor.
Now, you could write you key as that, to look for the preceding element where an ancestor does not exist:
<xsl:key name="group"
match="Step[not(starts-with(Obj, 'UC_'))]"
use="generate-id(ancestor::Step[starts-with(Obj, 'UC_')][1]
| self::*[not(ancestor::Step[starts-with(Obj, 'UC_')])]/preceding::Step[starts-with(Obj, 'UC_')][1])"/>
This does not look particularly nice though. You could slightly simplify things by having a separate key for the ones with no ancestor
<xsl:key name="group2"
match="Step[not(ancestor::Step[starts-with(Obj, 'UC_')])][not(starts-with(Obj, 'UC_'))]"
use="generate-id(preceding::Step[starts-with(Obj, 'UC_')][1])"/>
You would then just need to change your group
variable to use this key:
<xsl:variable name="group" select="key('group', generate-id())|key('group2', generate-id())"/>
As a final alternative, you may not really need a key to key the descendant nodes of a "UC_" value node. Instead, define a key to look up the preceding siblings on the same level....
<xsl:key name="group"
match="Step[not(starts-with(Obj, 'UC_'))]"
use="generate-id(preceding-sibling::Step[starts-with(Obj, 'UC_')][1])"/>
Then your group
variable is defined like so:
<xsl:variable name="group" select="descendant::Step|key('group', generate-id())/descendant-or-self::Step"/>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" version="5.0" doctype-system="about:legacy-compat" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="group"
match="Step[not(starts-with(Obj, 'UC_'))]"
use="generate-id(preceding-sibling::Step[starts-with(Obj, 'UC_')][1])"/>
<xsl:template match="Action">
<html>
<body>
<table border="1">
<xsl:apply-templates select="Step[starts-with(Obj, 'UC_')]"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Step[starts-with(Obj, 'UC_')]">
<xsl:variable name="group" select="descendant::Step|key('group', generate-id())/descendant-or-self::Step"/>
<tr>
<td rowspan="{1 + count($group)}">
<xsl:number count="Step[starts-with(Obj, 'UC_')]"/>
</td>
<td>
<xsl:value-of select="Obj"/>
</td>
</tr>
<xsl:apply-templates select="$group" />
</xsl:template>
<xsl:template match="Step[not(starts-with(Obj, 'UC_'))]">
<tr>
<td>
<xsl:value-of select="Obj"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1