Wrapping words from HTML using XSL - html

I need wrapping each word with a tag (e. span) in a HTML document, like:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div> Text in a div </div>
<div>
Text in a div
<p>
Text inside a p
</p>
</div>
</body>
</html>
To result something like this:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div> <span>Text </span> <span> in </span> <span> a </span> <span> div </span> </div>
<div>
<span>Text </span> <span> in </span> <span> a </span> <span> div </span>
<p>
<span>Text </span> <span> in </span> <span> a </span> <span> p </span>
</p>
</div>
</body>
</html>
It's important to keep the structure of the body...
Any help?

All of the three different solutions below use the XSLT design pattern of overriding the identity rule to generally preserve the structure and contents of the XML document, and only modify specific nodes.
I. XSLT 1.0 solution:
This short and simple transformation (no <xsl:choose> used anywhere):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(self::title)]/text()"
name="split">
<xsl:param name="pText" select=
"concat(normalize-space(.), ' ')"/>
<xsl:if test="string-length(normalize-space($pText)) >0">
<span>
<xsl:value-of select=
"substring-before($pText, ' ')"/>
</span>
<xsl:call-template name="split">
<xsl:with-param name="pText"
select="substring-after($pText, ' ')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied to the provided XML document:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div> Text in a div </div>
<div>
Text in a div
<p>
Text inside a p
</p>
</div>
</body>
</html>
produces the wanted, correct result:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
</div>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
<p>
<span>Text</span>
<span>inside</span>
<span>a</span>
<span>p</span>
</p>
</div>
</body>
</html>
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(self::title)]/text()">
<xsl:for-each select="tokenize(., '[\s]')[.]">
<span><xsl:sequence select="."/></span>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied to the same XML document (above), again the correct, wanted result is produced:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
</div>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
<p>
<span>Text</span>
<span>inside</span>
<span>a</span>
<span>p</span>
</p>
</div>
</body>
</html>
III Solution using FXSL:
Using the str-split-to-words template/function of FXSL one can easily implement much more complicated tokenization -- in any version of XSLT:
Let's have a more complicated XML document and tokenization rules:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div> Text: in a div </div>
<div>
Text; in; a. div
<p>
Text- inside [a] [p]
</p>
</div>
</body>
</html>
Here there is more than one delimiter that indicates the start or end of a word. In this particular example the delimiters can be: " ", ";", ".", ":", "-", "[", "]".
The following transformation uses FXSL for this more complicated tokenization:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext">
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(self::title)]/text()">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="normalize-space(.)"/>
<xsl:with-param name="pDelimiters"
select="' ;.:-[]'"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
</xsl:template>
<xsl:template match="word[string-length(normalize-space(.)) > 0]">
<span>
<xsl:value-of select="."/>
</span>
</xsl:template>
</xsl:stylesheet>
and produces the wanted, correct result:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
</div>
<div>
<span>Text</span>
<span>in</span>
<span>a</span>
<span>div</span>
<p>
<span>Text</span>
<span>inside</span>
<span>a</span>
<span>p</span>
<word/>
</p>
</div>
</body>
</html>

You could achieve this by extending the identity transform to include a recursive template which checks for spaces in a piece of text, and if so puts a span tag around the first word. It can then recursively calls itself for the remaining portion of the text.
Here is it in action...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Don't split the words in the title -->
<xsl:template match="title">
<xsl:copy-of select="." />
</xsl:template>
<!-- Matches a text element. Given a name so it can be recursively called -->
<xsl:template match="text()" name="wrapper">
<xsl:param name="text" select="." />
<xsl:variable name="new" select="normalize-space($text)" />
<xsl:choose>
<xsl:when test="contains($new, ' ')">
<span><xsl:value-of select="concat(substring-before($new, ' '), ' ')" /></span>
<xsl:call-template name="wrapper">
<xsl:with-param name="text" select="substring-after($new, ' ')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<span><xsl:value-of select="$new" /></span>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When called on your sample HTML, the output is as follows:
<html>
<head>
<title>It doesnt matter</title>
</head>
<body>
<div>
<span>Text </span>
<span>in </span>
<span>a </span>
<span>div</span>
</div>
<div>
<span>Text </span>
<span>in </span>
<span>a </span>
<span>div</span>
<p>
<span>Text </span>
<span>inside </span>
<span>a </span>
<span>p</span>
</p>
</div>
</body>
</html>
I wasn't 100% sure how important the spaces within the span elements are for you though.

Related

XSLT apply-templates in for-each

I'm trying to write a simple XHTML to Simple Docbook translator (the input XHTML is a limited subset so it should be doable).
I have this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" standalone="no"/>
<!--
<xsl:strip-space elements="*"/>
-->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!-- skip implicit tags-->
<xsl:template match="/html/body"><xsl:apply-templates/></xsl:template>
<xsl:template match="/html"><xsl:apply-templates/></xsl:template>
<!-- paragraphs to sections converter -->
<xsl:template match="h2">
<xsl:variable name="title" select="generate-id(.)"/>
<section>
<title><xsl:apply-templates select="text()"/></title>
<xsl:for-each select="following-sibling::*[generate-id(preceding-sibling::h2[1]) = $title and not(self::h2)]">
<xsl:apply-templates/>
</xsl:for-each>
</section>
</xsl:template>
<xsl:template match="p">
<para><xsl:apply-templates select="*|text()"/></para>
</xsl:template>
<xsl:template match="p[preceding-sibling::h2]"/>
<xsl:template match="ul">
<itemizedlist><xsl:apply-templates select="li"/></itemizedlist>
</xsl:template>
<xsl:template match="ul[preceding-sibling::h2]"/>
<xsl:template match="ol">
<orderedlist><xsl:apply-templates select="li"/></orderedlist>
</xsl:template>
<xsl:template match="ol[preceding-sibling::h2]"/>
<xsl:template match="li">
<listitem><para><xsl:apply-templates select="*|text()"/></para></listitem>
</xsl:template>
</xsl:stylesheet>
For this input
<html>
<body>
<p>First paragraph</p>
<p>Second paragraph</p>
<h2>First title</h2>
<p>First paragraph</p>
<p>Second paragraph</p>
<p>Third paragraph</p>
<h2>Second title</h2>
<p>First paragraph</p>
<ul>
<li>A list item</li>
<li>Another list item</li>
</ul>
<p>Second paragraph</p>
</body>
</html>
I expect this output
<para>First paragraph</para>
<para>Second paragraph</para>
<section>
<title>First title</title>
<para>First paragraph</para>
<para>Second paragraph</para>
<para>Third paragraph</para>
</section>
<section>
<title>Second title</title>
<para>First paragraph</para>
<itemizedlist>
<listitem>A list item</listitem>
<listitem>Another list item</listitem>
</itemizedlist>
<para>Second paragraph</para>
</section>
But I get
<para>First paragraph</para>
<para>Second paragraph</para>
<section><title>First title</title>First paragraphSecond paragraphThird paragraph</section>
<section><title>Second title</title>First paragraph
<listitem><para>A list item</para></listitem>
<listitem><para>Another list item</para></listitem>
Second paragraph</section>
For some reason, the template for my paragraphs and lists is not being applied. I'm guessing because the templates matching are the empty ones, but I need those to prevent duplicate tags outside section.
How can I make this work? TIA.
Use
<xsl:for-each select="following-sibling::*[generate-id(preceding-sibling::h2[1]) = $title and not(self::h2)]">
<xsl:apply-templates select="."/>
</xsl:for-each>
or simply
<xsl:apply-templates select="following-sibling::*[generate-id(preceding-sibling::h2[1]) = $title and not(self::h2)]"/>
to process those elements you want to wrap into a section. But there will be a collision with your other templates so perhaps using a mode helps for the processing:
<xsl:template match="p" mode="wrapped">
<para><xsl:apply-templates select="*|text()"/></para>
</xsl:template>
<xsl:template match="p[preceding-sibling::h2]"/>
<xsl:template match="ul" mode="wrapped">
<itemizedlist><xsl:apply-templates select="li"/></itemizedlist>
</xsl:template>
<xsl:template match="ul[preceding-sibling::h2]"/>
<xsl:template match="ol" mode="wrapped">
<orderedlist><xsl:apply-templates select="li"/></orderedlist>
</xsl:template>
<xsl:template match="ol[preceding-sibling::h2]"/>
<xsl:template match="li" mode="wrapped">
<listitem><para><xsl:apply-templates select="*|text()"/></para></listitem>
</xsl:template>

How can I replace HTML content using XSLT?

I would like to remove a certain text from an HTML page using XSLT. The text I would like to remove is <h2>OLD TEXT</h2>. I'm trying to remove it by replacing the text with an empty string but I don't get it to work.
When calling my string-replace-all function with "<h2>OLD TEXT</h2>" text as input I get the following error:
The value of the attribute "select" associated with an element type "xsl:with-param" must not contain the '<' character.
When calling my string-replace-all function with just "OLD TEXT" as input the text gets replaced but the output
is no longer HTML, it's just plain text without the HTML tags.
How could I do to replace <h2>OLD TEXT</h2> and still get the output in HTML format?
My code:
<?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:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="$text = '' or $replace = ''or not($replace)" >
<!-- Prevent this routine from hanging -->
<xsl:copy-of select="$text" />
</xsl:when>
<xsl:when test="contains($text, $replace)">
<xsl:copy-of select="substring-before($text,$replace)" />
<xsl:copy-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:variable name="updatedHtml">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="//div[#id='mainContent']" />
<xsl:with-param name="replace" select="'OLD TEXT'" />
<xsl:with-param name="by" select="''" />
</xsl:call-template>
</xsl:variable>
<xsl:template match="//div[#id='mainContent']">
<xsl:copy-of select="$updatedHtml" />
</xsl:template>
</xsl:stylesheet>
HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test page</title>
</head>
<body>
<div id="container">
<div id="mainContent">
<h1>Header one</h1>
<p>Some text</p>
<h2>OLD TEXT</h2>
<p>Some information about the old text</p>
<br>
More text
<h2>Another header</h2>
Some information <strong>with strong</strong> text.
<h2>Another header again</h2>
Some information <strong>with strong</strong> text.
</div>
</div>
</body>
</html>

How to wrap <h2> and <p> tags inside a <section> tag in xslt?

I want to wrap the headers and paragraph inside the section tags. Section tag ends when the next header arises.
Input:
<body>
<h2>text text</h2>
<p> some text </p>
<p> some text </p>
<h2> text text </h2>
<p> some text </p>
<p> some text </p>
<p> some text </p>
</body>
Output:
<body>
<section>
<h2>text text</h2>
<p> some text </p>
<p> some text </p>
</section>
<section>
<h2> text text </h2>
<p> some text </p>
<p> some text </p>
<p> some text </p>
</section>
</body>
Like mentioned in the comments, this is a grouping question.
If you're using XSLT 2.0, you can use xsl:for-each-group/#group-starting-with...
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/body">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each-group select="*" group-starting-with="h2">
<section>
<xsl:copy-of select="current-group()"/>
</section>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you're stuck with XSLT 1.0, you can use an xsl:key based on a generated id of the h2...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="sectElems" match="/body/*[not(self::h2)]"
use="generate-id(preceding-sibling::h2[1])"/>
<xsl:template match="/body">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="h2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="h2">
<xsl:variable name="id">
<xsl:value-of select="generate-id()"/>
</xsl:variable>
<section>
<xsl:copy-of select=".|key('sectElems',$id)"/>
</section>
</xsl:template>
</xsl:stylesheet>
Both of these stylesheets produce the same output.

My XSLT file doesnt make a proper HTML file

I am an amateur and have started XML and XSLT recently.
I've been asked to make a XSLT file from the below XML.
<?xml version="1.0" encoding="UTF-8" ?>
<event>
<title>Test 1</title>
<description>The first test</description>
<location>
<postalcode>A1A 1A1</postalcode>
<city>Vancouver</city>
<province>BC</province>
<streetaddress>Arina street east</streetaddress>
</location>
<attendees>
<name>John</name>
<email>example#gmail.com</email>
<phone>778777777</phone>
</attendees>
</event>
I made this XSLT file
<?xml version="1.0" encoding="utf-8"?>
<!-- event.xsl -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head><title><xsl:value-of select="title"/></title></head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="event">
<h2><xsl:value-of select="title"/></h2>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="description">
<p><xsl:value-of select="description"/></p>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="location">
<p>
<xsl:value-of select="streetaddress"/>
<xsl:value-of select="city"/>
<xsl:value-of select="province"/>
<xsl:value-of select="postalcode"/>
</p>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="attendees">
<xsl:for-each select="event/attendees">
<p>
<xsl:value-of select="name"/>
<xsl:value-of select="email"/>
<xsl:value-of select="phone"/>
</p>
</xsl:for-each>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
this is the generated HTML
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-16">
<title></title></head>
<body>
<h2>Test 1</h2>
Test 1
<p></p>The first test
<p>Arina street eastVancouverBCA1A 1A1</p>
A1A 1A1
Vancouver
BC
Arina street east
John
example#gmail.com
778777777
</body>
</html>
this the desired html that I am looking for
<html>
<head>
<title>Test 1</title>
<body>
<h2>Test 1</h2>
<p>The first test</p>
<p>
Ariana Street East<br>
Vancouver<br>
BC , A1A 1A1<br>
</p>
<!-- repeat-->
<p>
Name:john<br>
Email:example#gmail.com<br>
Phone:77877777
</p>
<p>
Name:john2<br>
Email:example2#gmail.com<br>
Phone:77877778
</p>
</body>
</html>
when I make a HTML file it is kind of messed up.
would you let me know where are my mistakes?
do you have any easy explained article?
thank you
You may try this slightly adapted version:
<?xml version="1.0" encoding="utf-8"?>
<!-- event.xsl -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<title>
<xsl:value-of select="event/title"/>
</title>
</head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="title" />
<xsl:template match="event">
<h2>
<xsl:value-of select="title"/>
</h2>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="description">
<p>
<xsl:value-of select="description"/>
</p>
</xsl:template>
<xsl:template match="location">
<p>
<xsl:value-of select="streetaddress"/>
<br/>
<xsl:value-of select="city"/>
<br/>
<xsl:value-of select="province"/>
<br/>
<xsl:value-of select="postalcode"/>
<br/>
</p>
</xsl:template>
<xsl:template match="attendees">
<p>
Name: <xsl:value-of select="name"/><br/>
Email: <xsl:value-of select="email"/><br/>
Phone: <xsl:value-of select="phone"/><br/>
</p>
</xsl:template>
</xsl:stylesheet>
Which will generate the following output:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test 1</title>
</head>
<body>
<h2>Test 1</h2>
<p></p>
<p>Arina street east<br>Vancouver<br>BC<br>A1A 1A1<br></p>
<p>
Name: John<br>
Email: example#gmail.com<br>
Phone: 778777777<br></p>
</body>
</html>
I think you have an error in "attendess" pattern match:
<xsl:template match="attendees">
<xsl:for-each select="event/attendees">
<p>...
The "for-each" instruction is redundant, attendees template is applied from de "apply-templates" instruction in the event template.
The first title section wont work, update it to:
<title><xsl:value-of select="event/title"/></title>
add field names at the begining and elements at the end of the lines on attendees and location
<xsl:template match="location">
<p>
address:<xsl:value-of select="streetaddress"/><br>
city:<xsl:value-of select="city"/><br>
province:<xsl:value-of select="province"/><br>
postalcode:<xsl:value-of select="postalcode"/> <br>
</p>
<xsl:apply-templates />
</xsl:template>

How to format unknown XML elements of a specific class with XSLT into HTML?

So I have a XML document generated by my application like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE AddressBook>
<?xml-stylesheet type="text/xsl" href="stylesheet.xsl"?>
<AddressBook>
<Item>
<UserGeneratedElementName1 class="info">Whatever blah blah</UserGeneratedElementName1>
<UserGeneratedElementName2 class="info">Whatever blah blah</UserGeneratedElementName2>
</Item>
<Item>
<UserGeneratedElementName3 class="info">Whatever blah blah</UserGeneratedElementName3>
</Item>
...
...
Other Items with user-generated elements with user-generated content...
</AddressBook>
And I want to turn it into a HTML document similar to this:
<html>
<head>
<title>AddressBook</title>
</head>
<body>
<div class="root">
<div class="item">
<b>UserGeneratedElementName1:</b> Whatever blah blah
<b>UserGeneratedElementName2:</b> Whatever blah blah
</div>
<div class="item">
<b>UserGeneratedElementName3:</b> Whatever blah blah
</div>
...
...
Other transformed items...
</div>
</body>
</html>
I have tried to get a grasp of the XSLT syntax, but all the guides were either too vague to help me with this or too deep. Also XSLT syntax seems quite confusing.
Thanks in advance.
Take a look at this question here
Is there an XSLT name-of element?
You can use
<xsl:value-of select ="name(.)"/>
or
<xsl:value-of select ="local-name()"/>
to get the name of a node, depending on if you want to include the full prefixed name, or just the local portion.
You should be able to piece those together with xsl:for-each blocks to iterate through the first 3 levels of items and generate the HTML you're looking for.
Something like this would work for a fixed number of levels.
<xsl:for-each select="*">
<html>
<head>
<title><xsl:value-of select="local-name()" /></title>
</head>
<body>
<div class="root">
<xsl:for-each select="*">
<div>
<xsl:attribute name="class">
<xsl:value-of select="local-name()" />
</xsl:attribute>
<xsl:for-each select="*">
<b><xsl:value-of select="local-name()" />:</b> <xsl:value-of select="." />
</xsl:for-each>
</div>
</xsl:for-each>
</div>
</body>
</html>
</xsl:for-each>
A more generic approach would look something more like:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes" version="4.0"/>
<xsl:template match="/">
<xsl:for-each select="*">
<html>
<head>
<title><xsl:value-of select="local-name()" /></title>
</head>
<body>
<div class="root">
<xsl:call-template name="recurseElement">
<xsl:with-param name="element" select="." />
</xsl:call-template>
</div>
</body>
</html>
</xsl:for-each>
</xsl:template>
<xsl:template name="recurseElement">
<xsl:param name="element" />
<xsl:for-each select="$element/*">
<xsl:choose>
<xsl:when test="count(child::*)>0">
<div>
<xsl:attribute name="class">
<xsl:value-of select="local-name()" />
</xsl:attribute>
<xsl:call-template name="recurseElement">
<xsl:with-param name="element" select="." />
</xsl:call-template>
</div>
</xsl:when>
<xsl:otherwise>
<b><xsl:value-of select="local-name()" />:</b> <xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This complete XSLT transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="AddressBook">
<html>
<head>
<title>AddressBook</title>
</head>
<body>
<div class="root">
<xsl:apply-templates/>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="Item">
<div class="item"><xsl:apply-templates/></div>
</xsl:template>
<xsl:template match="Item/*">
<b><xsl:value-of select="name()"/>:</b> <xsl:text/>
<xsl:value-of select="concat(.,'
')"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<AddressBook>
<Item>
<UserGeneratedElementName1 class="info">Whatever blah blah</UserGeneratedElementName1>
<UserGeneratedElementName2 class="info">Whatever blah blah</UserGeneratedElementName2>
</Item>
<Item>
<UserGeneratedElementName3 class="info">Whatever blah blah</UserGeneratedElementName3>
</Item> ... ... Other Items with user-generated elements with user-generated content...
</AddressBook>
produces the wanted, correct result:
<html>
<head>
<title>AddressBook</title>
</head>
<body>
<div class="root">
<div class="item">
<b>UserGeneratedElementName1:</b>Whatever blah blah
<b>UserGeneratedElementName2:</b>Whatever blah blah
</div>
<div class="item">
<b>UserGeneratedElementName3:</b>Whatever blah blah
</div> ... ... Other Items with user-generated elements with user-generated content...
</div>
</body>
</html>
Explanation:
Templates matching any Item element and any element child of an Item element.
Use of the standard XPath name() function.