Remove an attribute based on its value (specific entry's first child) - xslt

I'm trying to remove attribute whose value ="remove_it"
but not able to find a way
This is Input xml
<row>
<entry>
<p type="some1">
<t>This is text</t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
</row>
<row>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
</row>
<row>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
<entry>
<p type="remove_it">
<t> </t>
</p>
</entry>
</row>
</tbody>
</tgroup>
</table>
<p>
<t> </t>
</p>
</body>
</section>
want something like
<tgroup>
<tbody>
<row>
<entry>
<p type="some1">
<t>This is text</t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
</row>
<row>
<entry>
<p>
<t> </t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
</row>
<row>
<entry>
<p>
<t> </t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
<entry>
<p>
<t> </t>
</p>
</entry>
</row>
</tbody>
</tgroup>
</table>
<p>
<t> </t>
</p>
</body>
</section>
what i have been trying
<xsl:template match="sc:entry[(child::*[local-name()='p'][#type='_para'])]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sc:entry[(child::*[local-name()='p'][#type='_para'])]">
but this remove the entire node, I just want to remove attribute of p whose parent is entry
Is there any way I can only remove attribute of those value = "remove_it"
Thanks in advance!!

I just want to remove attribute of p whose parent is entry
Consider this example:
XML
<root>
<row>
<entry>
<p type="some1">
<t>This is text</t>
</p>
</entry>
<entry>
<p type="remove_it">
<t/>
</p>
</entry>
<entry>
<p type="remove_it">
<t/>
</p>
</entry>
</row>
<row>
<entry>
<p type="remove_it">
<t/>
</p>
</entry>
<entry>
<p type="keep_it">
<t/>
</p>
</entry>
<entry>
<p type="remove_it">
<t/>
</p>
</entry>
<entry>
<p type="remove_it">
<t/>
</p>
</entry>
</row>
</root>
XSLT 3.0
<xsl:stylesheet version="3.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:mode on-no-match="shallow-copy"/>
<xsl:template match="entry/p/#type[.='remove_it']"/>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<row>
<entry>
<p type="some1">
<t>This is text</t>
</p>
</entry>
<entry>
<p>
<t/>
</p>
</entry>
<entry>
<p>
<t/>
</p>
</entry>
</row>
<row>
<entry>
<p>
<t/>
</p>
</entry>
<entry>
<p type="keep_it">
<t/>
</p>
</entry>
<entry>
<p>
<t/>
</p>
</entry>
<entry>
<p>
<t/>
</p>
</entry>
</row>
</root>
Note:
You should never have to use a hack like *[local-name()='p']. If your input nodes are in a namespace, then deal with the namespace properly - see: XSLT Transform doesn't work until I remove root node.

Related

extract the value of an element node via XPath?

Source-XML:
<data>
<item>
<values>
<element1>
<language>EN</language>
<text>text</text>
</element1>
<element2>
<language>DE</language>
<text>Text</text>
</element2>
</values>
</item>
<item>
<values>
<element5>
<language>EN</language>
<text>description</text>
</element5>
<element6>
<language>DE</language>
<text>Beschreibung</text>
</element6>
</values>
</item> </data>
I want to get all the elements in language 'EN'. First I have a loop, where I saved the elment names in a variable. In the next step I want to get only the elements in language "EN". I need in the result of this step only the element-name and text which have the language 'EN' to build a table.
I tried this:
<xsl:param name="element" select="'element1'"/>
<xsl:template match="/">
<xsl:if test="data/item/values[local-name()=$element]/language[text()='EN']">
</xsl:if>
</xsl:template>
And the output XSLT should be something like:
<table id="123">
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1.0*"/>
<colspec colname="c2" colnum="2" colwidth="1.0*"/>
<thead>
<row>
<entry>Name</entry>
<entry>Values</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<ph>element1</ph>
</entry>
<entry>text</entry>
</row>
<row>
<entry>
<ph>element5</ph>
</entry>
<entry>description</entry>
</row>
</tbody>
</tgroup>
</table>
I know that there are other ways to solve this problem. But for other steps in the transformation it is important to test every element separately.
Thanks in advance!
<xsl:output method="xml" indent="yes"/>
<xsl:template match="data">
<xsl:for-each-group select="item" group-by="values/*[language = 'EN']">
<xsl:for-each select="current-group()">
<xsl:element name="{current-group()/values/*[language = 'EN']/local-name()}">
<language>
<xsl:value-of select="descendant::language[text() = 'EN']"/>
</language>
<txt>
<xsl:value-of select="current-group()/values/*[language = 'EN']/text"/>
</txt>
</xsl:element>
</xsl:for-each>
</xsl:for-each-group>
</xsl:template>
You may do like this
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common" version="1.0">
<xsl:output indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:variable name="ENelements">
<xsl:for-each select="/data/item/values/*[language='EN']">
<element name="{local-name()}"><xsl:value-of select="text" /></element>
</xsl:for-each>
</xsl:variable>
<table id="123">
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1.0*"/>
<colspec colname="c2" colnum="2" colwidth="1.0*"/>
<thead>
<row>
<entry>Name</entry>
<entry>Values</entry>
</row>
</thead>
<tbody>
<xsl:for-each select="exsl:node-set($ENelements)/*">
<row>
<entry>
<ph><xsl:value-of select="#name" /></ph>
</entry>
<entry><xsl:value-of select="." /></entry>
</row>
</xsl:for-each>
</tbody>
</tgroup>
</table>
</xsl:template>
</xsl:stylesheet>
http://xsltfiddle.liberty-development.net/jyRYYig
AFAICT, it could be simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/data">
<table id="123">
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1.0*"/>
<colspec colname="c2" colnum="2" colwidth="1.0*"/>
<thead>
<row>
<entry>Name</entry>
<entry>Values</entry>
</row>
</thead>
<tbody>
<xsl:for-each select="item">
<xsl:variable name="elem" select="values/*[language='EN']" />
<row>
<entry>
<ph>
<xsl:value-of select="name($elem)"/>
</ph>
</entry>
<entry>
<xsl:value-of select="$elem/text"/>
</entry>
</row>
</xsl:for-each>
</tbody>
</tgroup>
</table>
</xsl:template>
</xsl:stylesheet>

XSLT Merging Data

I have an xml file with two "sets" of data that I need to merge into a table showing old vs new. I'm doing this by processing only new data, and for each piece of new data, grabbing the accompanying old piece. My question is how to properly store my current position in the new set so that I can grab the matching data from the old set. The real challenge I'm having is around nested elements, where position() isn't as useful. So, given the following simplified xml, where I know 100% for sure that there will be a perfectly matching tag set in New and Old...
<doc>
<New>
<Para>New info 1</Para>
<Para>New info 2</Para>
<List>
<Li>New Point 1</Li>
<Li>New Point 2</Li>
<Li>New Point 3</Li>
</List>
<Para>New info 3</Para>
<List>
<Li>New Point 4</Li>
<Li>New Point 5</Li>
<Li>New Point 6
<List>
<Li>New nested Point 1</Li>
<Li>New nested Point 2</Li>
<Li>New nested Point 3</Li>
</List>
</Li>
</List>
</New>
<Old>
<Para>Old info 1</Para>
<Para>Old info 2</Para>
<List>
<Li>Old Point 1</Li>
<Li>Old Point 2</Li>
<Li>Old Point 3</Li>
</List>
<Para>Old info 3</Para>
<List>
<Li>Old Point 4</Li>
<Li>Old Point 5</Li>
<Li>Old Point 6
<List>
<Li>Old nested Point 1</Li>
<Li>Old nested Point 2</Li>
<Li>Old nested Point 3</Li>
</List>
</Li>
</List>
</Old>
I want to merge and output the info in an html-like table.
<table>
<row>
<entry>New Info 1</entry>
<entry>Old Info 1</entry>
</row>
<row>
<entry>New Info 2</entry>
<entry>Old Info 2</entry>
</row>
<row>
<entry>
<list>
<li>New Point 1</li>
</list>
</entry>
<entry>
<list>
<li>Old Point 1</li>
</list>
</entry>
</row>
<row>
<entry>
<list>
<li>New Point 2</li>
</list>
</entry>
<entry>
<list>
<li>Old Point 2</li>
</list>
</entry>
</row>
<row>
<entry>
<list>
<li>New Point 3</li>
</list>
</entry>
<entry>
<list>
<li>Old Point 3</li>
</list>
</entry>
</row>
<row>
<entry>New Info 3</entry>
<entry>Old Info 3</entry>
</row>
<row>
<entry>
<list>
<li>New Point 4</li>
</list>
</entry>
<entry>
<list>
<li>Old Point 4</li>
</list>
</entry>
</row>
<row>
<entry>
<list>
<li>New Point 5</li>
</list>
</entry>
<entry>
<list>
<li>Old Point 5</li>
</list>
</entry>
</row>
<row>
<entry>
<list>
<li>New Point 6</li>
</list>
</entry>
<entry>
<list>
<li>Old Point 6</li>
</list>
</entry>
</row>
<row>
<entry>
<list>
<li>New nested Point 1</li>
</list>
</entry>
<entry>
<list>
<li>Old nested Point 1</li >
</entry>
</row>
<row>
<entry>
<list>
<li>New nested Point 2</li>
</list>
</entry>
<entry>
<list>
<li>Old nested Point 2</li>
</list>
</entry>
</row>
<row>
<entry>
<list>
<li>New nested Point 3</li>
</list>
</entry>
<entry>
<list>
<li>Old nested Point 3</li>
</list>
</entry>
</row>
</table>
It's only nested elements that are problematic. For everything else I can store my position within <New> and apply templates on the corresponding <Old> data, but position() becomes less useful when nested.
Any ideas on how to effectively store my position within <New> so that I can process the matching <Old> element? I'm using XSLT 1.
Since your example only deals with the top-level elements in each branch, the requested result can be accomplished by:
XSLT 1.0
<xsl:stylesheet version="1.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:key name="old" match="id" use="." />
<xsl:variable name="old" select="/doc/Old" />
<xsl:template match="/doc">
<table>
<xsl:apply-templates select="New/Para | New/List"/>
</table>
</xsl:template>
<xsl:template match="Para">
<xsl:variable name="i" select="count(preceding-sibling::Para)" />
<row>
<entry>
<xsl:value-of select="."/>
</entry>
<entry>
<xsl:value-of select="$old/Para[$i + 1]"/>
</entry>
</row>
</xsl:template>
<xsl:template match="List">
<xsl:variable name="i" select="count(preceding-sibling::List)" />
<row>
<entry>
<xsl:copy-of select="."/>
</entry>
<entry>
<xsl:copy-of select="$old/List[$i + 1]"/>
</entry>
</row>
</xsl:template>
</xsl:stylesheet>
Matching individual leaf nodes by their position in their respective tree would be much more complicated, I think.
Edit:
I do need to match individual nodes unfortunately.
As I said, this is going to be much more complex - especially since you cannot use an extension function to dynamically evaluate a string into a path expression.
Here, we'll do a preliminary pass to process nodes in the Old branch and assign them a path attribute. This will enable us to call them by this attribute when processing the nodes in the New branch for the final output:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- first-pass -->
<xsl:variable name="old">
<xsl:apply-templates select="/doc/Old" mode="old"/>
</xsl:variable>
<xsl:variable name="old-set" select="exsl:node-set($old)" />
<xsl:template match="Para | Li" mode="old">
<node>
<xsl:attribute name="path">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="name()" />
<xsl:variable name="i" select="count(preceding-sibling::*[name()=name(current())])" />
<xsl:text>[</xsl:text>
<xsl:value-of select="count(preceding-sibling::*[name()=name(current())])" />
<xsl:text>]</xsl:text>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
<xsl:value-of select="text()"/>
</node>
<xsl:apply-templates select="*" mode="old"/>
</xsl:template>
<!-- output -->
<xsl:template match="/doc">
<table>
<xsl:apply-templates select="New"/>
</table>
</xsl:template>
<xsl:template match="Para | Li">
<xsl:variable name="path">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="name()" />
<xsl:variable name="i" select="count(preceding-sibling::*[name()=name(current())])" />
<xsl:text>[</xsl:text>
<xsl:value-of select="count(preceding-sibling::*[name()=name(current())])" />
<xsl:text>]</xsl:text>
<xsl:if test="position()!=last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="old-path" select="concat('doc[0]/Old[0]/', substring-after($path, 'doc[0]/New[0]/'))" />
<row>
<entry>
<xsl:value-of select="text()"/>
</entry>
<entry>
<xsl:value-of select="$old-set/node[#path=$old-path]"/>
</entry>
</row>
<xsl:apply-templates select="*"/>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<table>
<row>
<entry>New info 1</entry>
<entry>Old info 1</entry>
</row>
<row>
<entry>New info 2</entry>
<entry>Old info 2</entry>
</row>
<row>
<entry>New Point 1</entry>
<entry>Old Point 1</entry>
</row>
<row>
<entry>New Point 2</entry>
<entry>Old Point 2</entry>
</row>
<row>
<entry>New Point 3</entry>
<entry>Old Point 3</entry>
</row>
<row>
<entry>New info 3</entry>
<entry>Old info 3</entry>
</row>
<row>
<entry>New Point 4</entry>
<entry>Old Point 4</entry>
</row>
<row>
<entry>New Point 5</entry>
<entry>Old Point 5</entry>
</row>
<row>
<entry>New Point 6
</entry>
<entry>Old Point 6
</entry>
</row>
<row>
<entry>New nested Point 1</entry>
<entry>Old nested Point 1</entry>
</row>
<row>
<entry>New nested Point 2</entry>
<entry>Old nested Point 2</entry>
</row>
<row>
<entry>New nested Point 3</entry>
<entry>Old nested Point 3</entry>
</row>
</table>

Suppress default attributes when using xsl:copy-of

I cannot figure out how to suppress the default attribute shape="rect" in an xhtml output document when using xsl:copy-of.
Sample XML:
<div class="table">
<p class="table-caption"><span class="table-label">Table 7.2</span> Foo<a class="tabfn-ref"
href="#s9781483390086.i825" id="s9781483390086.i808" shape="rect"><sup>a</sup></a> in
Pricing</p>
<img alt="Table 11" class="table-image" src="10.4135_9781483390086-table11.jpg"/>
<table>
<tgroup cols="3">
<colspec align="left" colname="1" colnum="1"/>
<colspec align="left" colname="2" colnum="2"/>
<colspec align="left" colname="3" colnum="3"/>
<thead>
<row rowsep="1">
<entry align="left">
<p/>
</entry>
<entry align="left">
<p>N</p>
</entry>
<entry align="left">
<p>%</p>
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<p>Foo<a class="tabfn-ref" href="#s9781483390086.i826"
id="s9781483390086.i809" shape="rect"><sup>b</sup></a></p>
<p>1–12</p>
<p>13–24</p>
<p>25–36</p>
<p>37–48</p>
<p>49–60</p>
<p>61–72</p>
<p>73–84</p>
<p>85–96</p>
<p>More than 96</p>
</entry>
</row>
<row>
<entry>
<p>Bar<a class="tabfn-ref" href="#s9781483390086.i827"
id="s9781483390086.i810" shape="rect"><sup>c</sup></a></p>
<p>1–20%</p>
<p>21–40%</p>
<p>41–60%</p>
<p>61–80%</p>
<p>More than 80%</p>
</entry>
</row>
</tbody>
</tgroup>
</table>
<div class="table-notes" id="s9781483390086.i825a">
<p class="table-note" id="s9781483390086.i825b"><span class="label-fn"><a class="tabfn-ref"
href="#s9781483390086.i808" id="s9781483390086.i825" shape="rect"
><sup>a</sup></a></span> Foo</p>
<p class="table-note" id="s9781483390086.i825c"><span class="label-fn"><a class="tabfn-ref"
href="#s9781483390086.i809" id="s9781483390086.i826" shape="rect"
><sup>b</sup></a></span> Bar</p>
<p class="table-note" id="s9781483390086.i825d"><span class="label-fn"><a class="tabfn-ref"
href="#s9781483390086.i810" id="s9781483390086.i827" shape="rect"
><sup>c</sup></a></span> Too</p>
</div>
Sample XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xhtml="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="xs xhtml" xmlns="http://www.w3.org/1999/xhtml"
version="2.0">
<xsl:output method="xhtml" indent="yes"/>
<!-- Suppress default shape attribute -->
<xsl:template match="*/#shape"/>
<!-- Identity template -->
<xsl:template match="node()|#*">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template priority="1" match="//div[#class='table']//p[#class='table-caption']"/>
<xsl:template match="table">
<xsl:element name="table">
<xsl:attribute name="hidden" select="'hidden'"/>
<xsl:element name="caption">
<xsl:copy-of select="preceding-sibling::p[#class='table-caption'] | *[not(preceding::a/#shape)]"/>
</xsl:element>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I need to suppress the shape="rect" from the output. As you can see, it is suppressed from the output for all anchors, except for the one using xsl:copy-of. How can I specify suppression in the line:
<xsl:copy-of select="preceding-sibling::p[#class='table-caption'] | *[not(preceding::a/#shape)]"/>
This is obviously not working.
Output:
<div class="table">
<img alt="Table 11" class="table-image" src="10.4135_9781483390086-table11.jpg"/>
<table xmlns="http://www.w3.org/1999/xhtml" hidden="hidden">
<caption>
<p xmlns="" class="table-caption">
<span class="table-label">Table 7.2</span> Foo <a class="tabfn-ref"
href="#s9781483390086.i825" id="s9781483390086.i808" shape="rect">
<sup>a</sup>
</a> in Pricing </p>
</caption>
<tgroup xmlns="" cols="3">
<colspec align="left" colname="1" colnum="1"/>
<colspec align="left" colname="2" colnum="2"/>
<colspec align="left" colname="3" colnum="3"/>
<thead>
<row rowsep="1">
<entry align="left">
<p/>
</entry>
<entry align="left">
<p>N</p>
</entry>
<entry align="left">
<p>%</p>
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<p>Foo <a class="tabfn-ref" href="#s9781483390086.i826" id="s9781483390086.i809">
<sup>b</sup>
</a>
</p>
<p>1–12</p>
<p>13–24</p>
<p>25–36</p>
<p>37–48</p>
<p>49–60</p>
<p>61–72</p>
<p>73–84</p>
<p>85–96</p>
<p>More than 96</p>
</entry>
</row>
<row>
<entry>
<p>Bar <a class="tabfn-ref" href="#s9781483390086.i827" id="s9781483390086.i810">
<sup>c</sup>
</a>
</p>
<p>1–20%</p>
<p>21–40%</p>
<p>41–60%</p>
<p>61–80%</p>
<p>More than 80%</p>
</entry>
</row>
</tbody>
</tgroup>
</table>
<div class="table-notes" id="s9781483390086.i825a">
<p class="table-note" id="s9781483390086.i825b">
<span class="label-fn">
<a class="tabfn-ref" href="#s9781483390086.i808" id="s9781483390086.i825">
<sup>a</sup>
</a>
</span> Foo </p>
<p class="table-note" id="s9781483390086.i825c">
<span class="label-fn">
<a class="tabfn-ref" href="#s9781483390086.i809" id="s9781483390086.i826">
<sup>b</sup>
</a>
</span> Bar </p>
<p class="table-note" id="s9781483390086.i825d">
<span class="label-fn">
<a class="tabfn-ref" href="#s9781483390086.i810" id="s9781483390086.i827">
<sup>c</sup>
</a>
</span> Too </p>
</div>
</div>
In the first a class="tabfn-ref" the shape attribute is retained (due to copy-of). I need this to be suppressed.
You do not have the ability to exclude attributes or nodes when using xsl:copy-of. It performs a deep copy of the selected node.
Since you are already using the identity transformation with an empty template to suppress the #shape attribute and already have an empty template matching //div[#class='table']//p[#class='table-caption'], the easiest thing to do would be to change the <xsl:copy-of> to <xsl:apply-templates> with a mode and define a template in that mode to copy and apply-templates.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="xs xhtml" xmlns="http://www.w3.org/1999/xhtml"
version="2.0">
<xsl:output method="xhtml" indent="yes"/>
<!-- Suppress default shape attribute -->
<xsl:template match="*/#shape"/>
<!-- Identity template -->
<xsl:template match="node()|#*">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template priority="1"
match="//div[#class='table']//p[#class='table-caption']"/>
<xsl:template match="//div[#class='table']//p[#class='table-caption']"
mode="caption">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="table">
<table hidden="hidden">
<caption>
<xsl:apply-templates
select="preceding-sibling::p[#class='table-caption']
| *[not(preceding::a/#shape)]" mode="caption"/>
</caption>
<xsl:apply-templates/>
</table>
</xsl:template>
</xsl:stylesheet>

Create Cals table

i've the below XML document, where some cals table has to be created.
<table frame="none">
<tgroup cols="5" align="left" colsep="1" rowsep="1">
<colspec colwidth="20pt" colname="c1"/>
<colspec colwidth="70pt" colname="c2"/>
<colspec colwidth="10pt" colname="c3"/>
<colspec colwidth="20pt" colname="c4"/>
<colspec colwidth="75pt" colname="c5"/>
<thead>
<row>
<entry>
<para>Item</para>
</entry>
<entry align="center">
<para>Injury</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para>Percentage of loss of earning capacity</para>
</entry>
<entry>
<para/>
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<para>1.</para>
</entry>
<entry>
<para>Loss of 2 limbs ...................................................................</para>
</entry>
<entry morerows="7">
<para>}</para>
</entry>
<entry morerows="7">
<para>100</para>
</entry>
<entry morerows="7">
<para></para>
</entry>
</row>
<row>
<entry>
<para>2.</para>
</entry>
<entry>
<para>Loss of both hands or of all fingers and both thumbs ...</para>
</entry>
</row>
<row>
<entry>
<para>3.</para>
</entry>
<entry>
<para>Loss of both feet ................................................................</para>
</entry>
</row>
<row>
<entry>
<para>4.</para>
</entry>
<entry>
<para>Total loss of sight ...............................................................</para>
</entry>
</row>
<row>
<entry>
<para>5.</para>
</entry>
<entry>
<para>Total paralysis ....................................................................</para>
</entry>
</row>
<row>
<entry>
<para>6.</para>
</entry>
<entry>
<para>Injuries resulting in being permanently bedridden .......</para>
</entry>
</row>
<row>
<entry>
<para>7.</para>
</entry>
<entry>
<para>Paraplegia ..........................................................................</para>
</entry>
</row>
<row>
<entry>
<para>8.</para>
</entry>
<entry>
<para>Any other injury causing permanent total disablement ...</para>
</entry>
</row>
<row>
<entry>
<para>9.</para>
</entry>
<entry>
<para>Loss of arm at shoulder ....................................................</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para>75</para>
</entry>
<entry>
<para>80 (preferred hand)</para>
</entry>
</row>
<row>
<entry>
<para>10.</para>
</entry>
<entry>
<para>Ankylosis of shoulder joint—</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para/>
</entry>
<entry>
<para/>
</entry>
</row>
<row>
<entry>
<para/>
</entry>
<entry>
<para>in optimum position .....................................................</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para>35</para>
</entry>
<entry>
<para/>
</entry>
</row>
<row>
<entry>
<para/>
</entry>
<entry>
<para>in worst position ............................................................</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para>55</para>
</entry>
<entry>
<para/>
</entry>
</row>
<row>
<entry>
<para>11.</para>
</entry>
<entry>
<para>Loss of arm between elbow and shoulder .......................</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para>75</para>
</entry>
<entry>
<para>80 (preferred hand)</para>
</entry>
</row>
<row>
<entry>
<para>12.</para>
</entry>
<entry>
<para>Loss of arm at elbow ..........................................................</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para>75</para>
</entry>
<entry>
<para>80 (preferred hand)</para>
</entry>
</row>
<row>
<entry>
<para>13.</para>
</entry>
<entry>
<para>Ankylosis of the elbow joint—</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para/>
</entry>
<entry>
<para/>
</entry>
</row>
<row>
<entry>
<para/>
</entry>
<entry>
<para>in optimum position .....................................................</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para>30</para>
</entry>
<entry>
<para/>
</entry>
</row>
<row>
<entry>
<para/>
</entry>
<entry>
<para>in worst position ............................................................</para>
</entry>
<entry>
<para/>
</entry>
<entry>
<para>50</para>
</entry>
<entry>
<para/>
</entry>
</row>
<row>
<entry>
<para>14.</para>
</entry>
</row>
</tbody>
</tgroup>
</table>
here i've to create a table as shown in the below picture
i know its not a good idea to ask for code directly, i tried for normal tables(which were done), but i'm really unable to know how to do a cals table in xslt. please let me know how i can achieve doing these cals table.
Thanks
You say in a comment that you're looking for HTML output - well the input XML you're starting with has the right structure to convert directly into an HTML table with a 1-1 mapping of element names, you just need to turn the morerows="n" of the source format into a rowspan="n+1" in the HTML
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:strip-space elements="*" />
<xsl:output method="html" indent="yes" />
<!-- these nodes have the same names in HTML -->
<xsl:template match="table | thead | tbody">
<xsl:copy><xsl:apply-templates /></xsl:copy>
</xsl:template>
<!-- row becomes tr -->
<xsl:template match="row">
<tr><xsl:apply-templates /></tr>
</xsl:template>
<!-- entry becomes td -->
<xsl:template match="entry">
<td><xsl:apply-templates select="#morerows|node()" /></td>
</xsl:template>
<xsl:template match="entry/#morerows">
<xsl:attribute name="rowspan" select="1 + ." />
</xsl:template>
</xsl:stylesheet>
We rely here on the default template rules for nodes that don't have an explicit template, which will process children recursively in the case of elements and output the text in the case of text nodes.
You've tagged the question XSLT 2.0 but for the record this is almost an XSLT 1.0 stylesheet, the only thing you'd have to change is that xsl:attribute doesn't take a select in 1.0, so instead of <xsl:attribute name="rowspan" select="..." /> you'd need <xsl:attribute name="rowspan"><xsl:value-of select="..." /></xsl:attribute>.

XSLT 1.0 grouping childs when followed by a certain node

Input xml:
<entry>
<text>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<author>abc</author>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<author>abc</author>
</text>
</entry>
I'm using XSLT 1.0.
I would like to select all <p> elements until the next <author> element and group them (together with the next <author> element) under a new <div> element. So expected output look like this:
<entry>
<text>
<div>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<author>abc</author>
</div>
<div>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<author>abc</author>
</div>
</text>
</entry>
I tried this solution:
<xsl:template match="entry">
<entry>
<text>
<div>
<xsl:apply-templates select="child::node()[not(preceding-sibling::author)]"/>
</div>
</text>
</entry>
</xsl:template>
which works fine for the first group of <p> + <author>, but not for the next group(s).
I would appreciate any help.
You may group all elements before an author (preceding-sibling:* which are not author (name() != 'author') and have the current author as next following author
(generate-id( following-sibling::author[1]) = generate-id(current())):
preceding-sibling::*[ name() != 'author' and
generate-id( following-sibling::author[1]) = generate-id(current()) ]
Try something like this:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text">
<xsl:copy>
<xsl:apply-templates select="author" mode="adddiv"/>
</xsl:copy>
</xsl:template>
<xsl:template match="author" mode="adddiv" >
<!-- preceding siblings not author -->
<xsl:variable name="fs" select="preceding-sibling::*[ name() != 'author' and
generate-id( following-sibling::author[1]
) = generate-id(current()) ]" />
<div >
<xsl:apply-templates select="$fs | ." />
</div>
</xsl:template>
</xsl:stylesheet>
Which will generate the following output:
<entry>
<text>
<div>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<author>abc</author>
</div>
<div>
<p>xyz</p>
<p>xyz</p>
<p>xyz</p>
<author>abc</author>
</div>
</text>
</entry>