XSLT: find first element above selected element - xslt

I want to get the first heading (h1) before a table in a docx.
I can get all headings with:
<xsl:template match="w:p[w:pPr/w:pStyle[#w:val='berschrift1']]">
<p>
<context>
<xsl:value-of select="." />
</context>
</p>
</xsl:template>
and I can also get all tables
<xsl:template match="w:tbl">
<p>
<table>
<xsl:value-of select="." />
</table>
</p>
</xsl:template>
But unfortunetly the processor does not accept
<xsl:template match="w:tbl/preceding-sibling::w:p[w:pPr/w:pStyle[#w:val='berschrift1']]">
<p>
<table>
<xsl:value-of select="." />
</table>
</p>
</xsl:template>
Here is a reduced XML file extracted from a docx: http://pastebin.com/KbUyzRVv
I want something like that as a result:
<context>Let’s get it on</context> <- my heading
<table>data</table>
<context>Let’s get it on</context> <- my heading
<table>data</table>
<context>We’re in the middle of something</context> <- my heading
<table>data</table>
Thanks to Daniel Haley I was able to find a solution for that problem. I'll post it here, so it is independend of the pastebin I postet below.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:v="urn:schemas-microsoft-com:vml" exclude-result-prefixes="xsl w v">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="w:tbl">
<context>
<xsl:value-of select="(preceding-sibling::w:p[w:pPr/w:pStyle[#w:val = 'berschrift1']])[last()]"/>
</context>
<table>
<xsl:value-of select="."/>
</table>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>

Hard to answer without a Minimal, Complete, and Verifiable example, but try this:
<xsl:template match="w:tbl">
<p>
<table>
<xsl:value-of select="(preceding::w:p[w:pPr/w:pStyle[#w:val='berschrift1']])[last()]"/>
</table>
</p>
</xsl:template>

Assuming you can use XSLT 2.0 (and most people can, nowadays), I find a useful technique here is to have a global variable that selects all the relevant nodes:
<xsl:variable name="special"
select="//w:tbl/preceding-sibling::w:p[w:pPr/w:pStyle[#w:val='berschrift1']][1]"/>
and then use this variable in a template rule:
<xsl:template match="w:p[. intersect $special]"/>
In XSLT 3.0 you can reduce this to
<xsl:template match="$special"/>

Related

XSLT - how to find sum of attribute of one node list where attribute value exists in different node list

I have a problem I am running in to with a XSLT 1.0 stylesheet that I imagine is probably fairly simple but I can't seem to quite wrap my mind around it. So I have XML that looks something like this
<Components>
<Component Quantity="2.0" MaterialCode="1111"/>
<DispensingInstructions/>
<ChargeContainers>
<ChargeContainer BarcodeValue="def"/>
<ChargeContainer BarcodeValue="jkl"/>
</ChargeContainers>
<Comments/>
<Transactions/>
<DispenseContainers>
<Container BarcodeValue="abc" Quantity="1.0"></Container>
<Container BarcodeValue="def" Quantity="2.0"></Container>
<Container BarcodeValue="ghi" Quantity="3.0"></Container>
<Container BarcodeValue="jkl" Quantity="4.0"></Container>
</DispenseContainers>
</Component>
<Component Quantity="1.0" MaterialCode="12345"/>
<DispensingInstructions/>
<ChargeContainers/>
<Comments/>
<Transactions/>
<DispenseContainers/>
</Component>
</Components>
I am trying to get a sum of the "quantity" attribute of DispenseContainers, but only if the container barcode exists in ChargeContainers. So for this example, I would want a function that returned 6 because ChargeContainers has both a node with barcode "def" and a node with barcode "jkl" and DispenseContainers those have quantities of 2 and 4 which sum to 6.
I had some XSL that looks something like this:
<xsl:for-each select="//Component">
<!--
Place holder for code to make table row with a bunch of info from nodelists of component
-->
<td>
<xsl:variable name="TotalDispensedQuantity" select="sum(DispenseContainers/Container/#Quantity)" />
<xsl:if test="$TotalDispensedQuantity != number(#Quantity)">
<xsl:attribute name="style">background-color: red;</xsl:attribute>
</xsl:if>
</td>
</xsl:for-each>
but now I have to cross reference the dispense containers to ensure they exist in charge containers before I add their quantity to the sum. I attempted to add the key as stated in the answers like so:
<xsl:for-each select="//Component">
<!--Get sum of dispensed containers quantities if they exist in charged containers list-->
<xsl:key name="k1" match="//ChargeContainers/ChargeContainer" use="#BarcodeValue" />
<xsl:template match=".">
<result>
<xsl:variable name="TotalChargedQuantity" select = "sum(DispenseContainers/Container[key('k1', #BarcodeValue)]/#Quantity)"/>
</result>
</xsl:template>
<!--
Place holder for code to make table row with a bunch of info from nodelists of component
-->
<td>
<xsl:if test="$TotalChargeQuantity != number(#Quantity)">
<xsl:attribute name="style">background-color: red;</xsl:attribute>
</xsl:if>
</td>
</xsl:for-each>
however, I get an error stating that key can't be a child element of for-each. Is there a...loop friendly way of doing this?
Edit1: I'm going to rewrite the XML to be a bit closer to real as well as give a bit more context. The solution given is probably correct, but I'm getting an error that key can't be a child of for-each
Use a key to resolve cross-references - for example:
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:key name="k1" match="NodeList1/Node" use="#name" />
<xsl:template match="/Root">
<result>
<xsl:value-of select="sum(NodeList2/Node[key('k1', #name)]/#value)"/>
</result>
</xsl:template>
</xsl:stylesheet>
Added:
Your edited question is so different from the original, it should have been posted as a new one.
In order to limit the action of the key to the current Component node, you need to add a unique identifier of the current Component to the key:
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:key name="k1" match="ChargeContainer" use="concat(generate-id(ancestor::Component), '|', #BarcodeValue)" />
<xsl:template match="/Components">
<table>
<tr>
<th>Material Code</th>
<th>Your Result</th>
</tr>
<xsl:for-each select="Component">
<xsl:variable name="component-id" select="generate-id()" />
<tr>
<td>
<xsl:value-of select="#MaterialCode"/>
</td>
<td>
<xsl:value-of select="sum(DispenseContainers/Container[key('k1', concat($component-id, '|', #BarcodeValue))]/#Quantity)"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Demo (using a well-formed (!) XML as the input): https://xsltfiddle.liberty-development.net/nb9PtDR

Template declared to match element, but never triggered

I've the following XML.
<?xml version="1.0" encoding="UTF-8"?>
<docs>
<biblos>
<texto xmlns="http://www.aranzadi.es/namespace/contenido/biblos/texto" idioma="spa">
<parrafo>
<en-origen estilo-fuente="cursiva">This is cursive text.</en-origen>
</parrafo>
</texto>
</biblos>
</docs>
and the following XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<body>
<section class="chapter">
<xsl:apply-templates select="docs"/>
</section>
</body>
</html>
</xsl:template>
<xsl:template match="docs">
<div class="chapter">
<xsl:text>Docs Block</xsl:text>
<xsl:apply-templates select="biblos"/>
</div>
</xsl:template>
<xsl:template match="biblos">
<xsl:text>biblos block</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="texto">
<xsl:text>Text To Block</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="parrafo">
<div class="para">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="parrafo">
<span class="format-smallcaps">
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="en-origen">
<xsl:variable name="fontStyle">
<xsl:choose>
<xsl:when test="./#estilo-fuente">
<xsl:value-of select="concat('font-style-',#estilo-fuente)"/>
</xsl:when>
<xsl:when test="./#format">
<xsl:value-of select="concat('format-',#format)"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<span class="{$fontStyle}">
<xsl:value-of select="."/>
<xsl:apply-templates select="para"/>
</span>
</xsl:template>
</xsl:transform>
when i run this, I'm getting the below output.
<!DOCTYPE html
PUBLIC "XSLT-compat">
<html>
<body>
<section class="chapter">
<div class="chapter">Docs Blockbiblos block
This is cursive text.
</div>
</section>
</body>
</html>
Here the problem is, though I've declared texto and child nodes of it in my XSLT, it is not getting called, but the text is directly getting printed.
Please let me know where I'm going wrong and how I can fix it.
Good question (thanks for providing a complete, working example!). Often, if elements are not matched, the cause lies in missing namespaces:
You have the following in your input XML:
<texto xmlns="http://www.aranzadi.es/namespace/contenido/biblos/texto" idioma="spa">
In other words, the texto element is in a namespace. In your XSLT you have the following:
<xsl:template match="texto">
Since no namespace is declared for XPath (xpath-default-namespace on the containing xsl:template or xsl:stylesheet), this will operate on elements texto in no namespace, meaning, as written, it will not match texto from your source.
You can solve this by:
<xsl:transform
xmlns:tto="http://www.aranzadi.es/namespace/contenido/biblos/texto"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
and:
<xsl:template match="tto:texto">
Now your template will be matched.
Remember that element names can be in a namespace if the namespace is declared on that element, but the attributes, unless prefixes, are in no nammespace, so this solution is only required (given your example input) on matching or selecting elements.
Also, it is important to realize that prefixes do not matter, they do not need to match the prefix (or absence thereof) from the source document. What matters, is that the namespace bound to the prefix matches.
If there are child elements, in this case parrafo and en-origen, these inherit the namespace given on their parent element. So if you want to match these elements as well, you should adjust their names to tto:paraffo and tto:en-origin in patterns and XPath expressions.

Index of the node selected not based on his parent

I have an xml like this one:
<?xml version="1.0" encoding="WINDOWS-1252" ?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<root>
<something>
<cd>a1</cd>
<cd>a2</cd>
</something>
<another>
<cd>b1</cd>
<cd>b2</cd>
<cd>b3</cd>
<cd>b1</cd>
<cd>b2</cd>
<cd>b3</cd>
<cd>b1</cd>
<cd>b2</cd>
</another>
</root>
I'm selecting all the cd nodes and trying to take the cds from 10 to 10 (1st, 11th, 21th...) this way:
<?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><body>
<xsl:apply-templates select="//cd" />
</body></html>
</xsl:template>
<xsl:template match="cd"/>
<xsl:template match="cd[position() mod 10 = 1]">
<div>
pos:<xsl:value-of select="position()"/>;
value:<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:stylesheet>
The output:
pos:1; value:a1
pos:3; value:b3
And the expected output:
pos:1; value:a1
pos:11; value:b11
The problem is that position() in the match is the node number based on his parent. So the position() of "b1" is 1 and not 3. Is there a function to know the index of the current node?
Thanks in advance.
//EDIT to extend the example
You can get a more accurate count by using xsl:number:
<xsl:template match="cd">
<div>pos: <xsl:number level="any"/></div>
</xsl:template>
You could also count the preceding cd elements:
<xsl:template match="cd">
<div>pos: <xsl:value-of select="count(preceding::cd)+1"/></div>
</xsl:template>
I prefer xsl:number, but I suppose it depends on how you're using the value. If you give a better example of what you're trying to do with that number, I can update my answer.
Example based on question update:
Input
<root>
<something>
<cd>a1</cd>
<cd>a2</cd>
</something>
<another>
<cd>b1</cd>
<cd>b2</cd>
<cd>b3</cd>
<cd>b1</cd>
<cd>b2</cd>
<cd>b3</cd>
<cd>b1</cd>
<cd>b2</cd>
</another>
<another>
<cd>c1</cd>
<cd>c2</cd>
<cd>c3</cd>
<cd>c1</cd>
<cd>c2</cd>
<cd>c3</cd>
<cd>c1</cd>
<cd>c2</cd>
</another>
</root>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html><body>
<xsl:apply-templates select="//cd" />
</body></html>
</xsl:template>
<xsl:template match="cd"/>
<xsl:template match="cd[(count(preceding::cd)+1) mod 10 = 1]">
<div>
pos:<xsl:value-of select="position()"/>;
value:<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:stylesheet>
Output
<html>
<body>
<div>
pos:1;
value:a1
</div>
<div>
pos:11;
value:c1
</div>
</body>
</html>
The position() in the match of template filters "cd"s based on position in its parent:
<xsl:template match="cd[position() mod (10) = 1]">
and the position() inside the template definition,
<div>pos:<xsl:value-of select="position()"/></div>
gives you the cd's sequence which the template has been applied for.
Thus, "position() mod (10) = 1" filters only those "cd"s which are 1st, 11th, 21st, etc. children of their parents.
Hence, you can remove the filter from xsl:template's match and put it inside like this:
<xsl:template match="cd">
<div>pos:<xsl:if test="position() mod 10 = 1">
<xsl:value-of select="position()"/>
</xsl:if>
</div>
</xsl:template>
This will save all the CDs in a variable. In the for-each, the context does not depend on the parent nodes so you will get the position among all CDs:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:variable name="all-cds" select="//cd" />
<xsl:template match="root">
<xsl:for-each select="$all-cds">
<div>pos: <xsl:value-of select="position()"/></div>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

create HTML from a list of XML nodes using XSLT

I am a noob on XSLT.
I have a XML where t nodes are followed by other nodes, and then another t node might appear again followed by nodes again, and so on
<t />
<n1 />
<n2 />
..
<t/>
<n3 />
<n4 />
...
What I need to turn this XML into is a HTML where t nodes wraps all nodes following it up to the next t node
<div class='t'>
<div class='n1'/>
<div class='n2'/>
...
</div>
<div class='t'>
<div class='n3'/>
<div class='n4'/>
...
</div>
I am having a hard time implementing this.
Any ideas \ hints?
Thanks!
This is grouping adjacents. There are many solutions:
Whit this wellformed input:
<root>
<t />
<n1 />
<n2 />
<t/>
<n3 />
<n4 />
</root>
XSLT 1.0: traversing with following axis
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="node()[1]" mode="group"/>
<xsl:apply-templates select="t"/>
</xsl:copy>
</xsl:template>
<xsl:template match="t">
<div class="t">
<xsl:apply-templates select="following-sibling::node()[1]"
mode="group"/>
</div>
</xsl:template>
<xsl:template match="t" mode="group"/>
<xsl:template match="node()" mode="group">
<xsl:apply-templates select="."/>
<xsl:apply-templates select="following-sibling::node()[1]"
mode="group"/>
</xsl:template>
<xsl:template match="*[starts-with(name(),'n')]">
<div class="{name()}"/>
</xsl:template>
</xsl:stylesheet>
XSLT 1.0: Keys
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:key name="kNodeByMark"
match="node()[../t][not(self::t)]"
use="generate-id((..|preceding-sibling::t[1])[last()])"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="key('kNodeByMark',generate-id())"/>
<xsl:for-each select="t">
<div class="t">
<xsl:apply-templates
select="key('kNodeByMark',generate-id())"/>
</div>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(),'n')]">
<div class="{name()}"/>
</xsl:template>
</xsl:stylesheet>
XSLT 2.0: for-each-group instruction
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="node()[../t[1] >> .]"/>
<xsl:for-each-group select="node()" group-starting-with="t">
<div class="t">
<xsl:apply-templates
select="current-group()[position()>1]"/>
</div>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(),'n')]">
<div class="{name()}"/>
</xsl:template>
</xsl:stylesheet>
Output:
<root>
<div class="t">
<div class="n1" />
<div class="n2" />
</div>
<div class="t">
<div class="n3" />
<div class="n4" />
</div>
</root>
EDIT: Traversing following axis refactored to look like the others solutions. Stripping identity rules.
See my note on your question, regarding "which XSLT version?". If grouping is supported in your target version, see other answers here, as that is easier to understand and will almost certainly perform better on any XSLT processor. If you aren't certain, I recommend going with a 1.0 solution like this one.
You can do it with the "XML fragment" exactly like you posted with most XSLT processors, but I added a "root" element to your XML, to reduce certain unknowns in answering your question.
In this solution below, I've tried to keep a direct correlation between the shape of the XSLT and the shape of the output you desire. In my opinion that makes it easier to maintain/understand, at least for smaller stylesheets.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/root">
<xsl:for-each select="t">
<div class='t'>
<xsl:for-each select="following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)]">
<div class='{name()}' />
</xsl:for-each>
</div>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The right-hand side of "following-sibling::*[count(preceding-sibling::t)=(count(current()/preceding-sibling::t) + 1) and not(self::t)]" could be simplified, I'm sure, using something like "current()::position()" (which isn't valid, fyi), but I'm rusty and couldn't remember some of the alias syntax.
This basically says: 1) Evaluate every T. 2) Select elements with the same quantity of T preceding them, as the index of the T we are currently evaluating.
Note that you've probably tried iterating through procedurally, and found you can't store the last value found in XSLT. Or you've found that you can, but only with nested templates. This same type of pivot you are performing has many XSLT neophytes hitting roadblocks, so don't feel bad.

Output element in comments

I need to display HTML-element in comments (for example)
<!-- <img src="path" width="100px" height="100px"/> -->
I use this approach
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="no" encoding="windows-1251"/>
<xsl:template match="myNode">
...
<xsl:comment><xsl:apply-templates select="image" /></xsl:comment>
...
</xsl:template>
<xsl:template match="image">
<img src="{#src}" width="{#width}px" height="{#height}px" />
</xsl:template>
</xsl:stylesheet>
As a result:
<!---->
that is the code in the element xsl:comment ignored.
How do I display an item in the comments?
It might be possible to replace
<xsl:comment><xsl:apply-templates select="image" /></xsl:comment>
with
<xsl:text disable-output-escaping="yes"><!--</xsl:text>
<xsl:apply-templates select="image" />
<xsl:text disable-output-escaping="yes">--></xsl:text>
Haven't tried though.
<xsl:comment><xsl:apply-templates select="image" /></xsl:comment>
As a result:
<!---->
that is the code in the element
xsl:comment ignored
The XSLT 1.0 Spec says:
It is an error if instantiating the
content of xsl:comment creates nodes
other than text nodes. An XSLT
processor may signal the error; if it
does not signal the error, it must
recover by ignoring the offending
nodes together with their content.
How do I display an item in the
comments?
It depends what is meant for "display": in a browser:
<-- <xsl:apply-templates select="image" /> -->
may be useful, provided the result of <xsl:apply-templates/> aboveis just simple text (not markup).
If to "display" means to provide the result as text, then DOE, if allowed by the XSLT processor, may give us the wanted result:
<--
Some text -->
Finally, if it is required that what should be inside the "comment" should be markup and it should be displayed as markup, then this is rather challenging. In this case one has to use:
<xsl:output method="text"/>
and should present every XML lexical item with its desired serialization (i.e. escaped).
This is how the XPath Visualizer constructs its output.
Here is a small transformation that demonstrates the first two approaches:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<-- Hello, World -->
<xsl:text disable-output-escaping="yes"><--</xsl:text>
Hello,world! --<xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:template>
</xsl:stylesheet>
this transformation, when applied on any XML document (not used), produces:
<-- Hello, World -->
<--
Hello,world! -->
Both "comments" may be viewed as comments in a browser, while only the second is presented as comment in free text.
The third approach (most probably what you want) is illustrated below:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<-- <xsl:apply-templates select="image"/> -->
</xsl:template>
<xsl:template match="image">
<img src="<xsl:value-of select="#src"/>"
width="<xsl:value-of select="#width"/>px"
height="<xsl:value-of select="#height"/>px"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<image src="http://example.com/yyy.jpg" width="200" height="300"/>
the wanted result is produced:
<--
<img src="http://example.com/yyy.jpg"
width="200px"
height="300px"/>
-->
viewed in a browser as:
<--
<img src="http://example.com/yyy.jpg"
width="200px"
height="300px"/>
-->
From http://www.w3.org/TR/xslt#section-Creating-Comments:
The xsl:comment element is instantiated to create a comment node in the result tree. The content of the xsl:comment element is a template for the string-value of the comment node.
For example, this
<xsl:comment>This file is
automatically generated. Do not
edit!</xsl:comment>
would create the comment
<!--This file is automatically
generated. Do not edit!-->
It is an error if instantiating the
content of xsl:comment creates nodes
other than text nodes. An XSLT
processor may signal the error; if it
does not signal the error, it must
recover by ignoring the offending
nodes together with their content.
It is an error if the result of
instantiating the content of the
xsl:comment contains the string -- or
ends with -. An XSLT processor may
signal the error; if it does not
signal the error, it must recover by
inserting a space after any occurrence
of - that is followed by another - or
that ends the comment.
So, in order to do what you want you need to use DOE mechanism.
As example, this stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="no" encoding="windows-1251"/>
<xsl:template match="img">
<img src="{.}"/>
</xsl:template>
<xsl:template match="root">
<xsl:variable name="vResult">
<xsl:apply-templates/>
</xsl:variable>
<html>
<xsl:copy-of select="$vResult"/>
<xsl:comment>
<xsl:apply-templates select="msxsl:node-set($vResult)"
mode="encode"/>
</xsl:comment>
</html>
</xsl:template>
<xsl:template match="*" mode="encode">
<xsl:value-of select="concat('<',name())"
disable-output-escaping="yes"/>
<xsl:apply-templates select="#*" mode="encode"/>
<xsl:text>></xsl:text>
<xsl:apply-templates mode="encode"/>
<xsl:value-of select="concat('<',name(),'>')"
disable-output-escaping="yes"/>
</xsl:template>
<xsl:template match="*[not(node())]" mode="encode">
<xsl:value-of select="concat('<',name())"
disable-output-escaping="yes"/>
<xsl:apply-templates select="#*" mode="encode"/>
<xsl:text>/></xsl:text>
</xsl:template>
<xsl:template match="#*" mode="encode">
<xsl:value-of select="concat(' ',name(),'="',.,'"')"/>
</xsl:template>
</xsl:stylesheet>
With this input:
<root>
<img>http://example.org/image1.jpg</img>
<img>http://example.org/image2.jpg</img>
<img>http://example.org/image3.jpg</img>
</root>
Output:
<html>
<img src="http://example.org/image1.jpg">
<img src="http://example.org/image2.jpg">
<img src="http://example.org/image3.jpg">
<!--<img src="http://example.org/image1.jpg"/>
<img src="http://example.org/image2.jpg"/>
<img src="http://example.org/image3.jpg"/>-->
</html>
Note: node-set extension function for two pass transformation. disable-output-escaping attribute for xsl:value-of instruction.
As said before by Dimitri you can't use the xsl:comment instruction.
If your purpose is simply to comment a fragment of tree, the simplest way is to put the comments markers as text (unescaped) like this :
<xsl:text disable-output-escaping="yes"><!--</xsl:text><xsl:apply-templates select="image" /><xsl:text disable-output-escaping="yes">--></xsl:text>
instead of :
<xsl:comment><xsl:apply-templates select="image" /></xsl:comment>
and you will obtain exactly this
<!-- <img src="path" width="100px" height="100px"/> -->
used with msxml and saxon