I have some XML which looks like this:
<doc>
<elem>
Some text, <i class="i">including HTML markup</i>
<item value="1">
Some more text, <i>including HTML markup</i>
</item>
<item value="2">
Yet more text, <i>including HTML markup</i>
</item>
</elem>
</doc>
I want to turn it into HTML consisting of an ordered list of the content of the <elem>
tags followed by a bulleted list of the content of the <item>
elements, like this:
<ol>
<li>
Some text, <i class="i">including HTML markup</i>
<ul>
<li>Some more text, <i>including HTML markup</i></li>
<li>Yet more text, <i>including HTML markup</i></li>
</ul>
</li>
</ol>
My XSLT stylesheet looks like this:
<?xml version='1.0'?>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match='doc'>
<ol>
<xsl:apply-templates select='elem'/>
</ol>
</xsl:template>
<xsl:template match='elem'>
<li>
<xsl:apply-templates select='*[not(elem|item)]|text()'/>
<ul>
<xsl:apply-templates select='item'/>
</ul>
</li>
</xsl:template>
<xsl:template match='item'>
<li>
<xsl:apply-templates/>
</ul>
</li>
</xsl:template>
<xsl:template match="*[not(elem|item)]|text()">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is the result:
<ol>
<li>
Some text, <i>including HTML markup</i>
<li>
Some more text, <i>including HTML markup</i>
</li>
<li>
Yet more text, <i>including HTML markup</i>
</li>
<ul>
<li>
Some more text, <i>including HTML markup</i>
</li>
<li>
Yet more text, <i>including HTML markup</i>
</li>
</ul>
</li>
</ol>
Despite the selector not(elem|item)
in the rule for elem
, it is applying the template for item
before it generates the <ul>
tag, and it is losing the class
attribute on the <i>
tag.
What am I doing wrong here?
The expression
<xsl:apply-templates select='*[not(elem|item)]|text()'/>
doesn't do what you think it does.
I suspect that what you are trying to do is to process all child elements and text nodes other than elem
and item
children (though in your example there aren't any child elements other than item
). If that's your intent you can do *[not(self::elem or self::item)]
. What your expression actually does is to select all child elements that don't have a child called elem
or item
.