Positional grouping: xslt conversion of html - xslt

I have the following html file and i want to run a transformation so that all the h1,h2,h3 tags will converted into corresponding divs. h2 will always be a nested div of h1 and if there are 2 h2 tags then it should have there own divs. same way h3 will always be a nested div of h2.
<body>
<p> this is a text</p>
click here
<h3>this is heading 3</h3>
<p>text for heading 3</p>
<h1>
heading 1
</h1>
this is a text for heading 1
This is a link
<h2>
this is heading 2
</h2>
this is a text for heading 2
<h2>
this is heading 2 again
</h2>
this is a text for heading 2 again
</body>
"
The output of above should be like :
<body>
<p> this is a text</p>
click here
<div>
<heading>this is heading 3</heading>
<p>text for heading 3</p>
<div>
<div>
<heading>
heading 1
</heading>
this is a text for heading 1
This is a link
<div>
<heading>
this is heading 2
</heading>
this is a text for heading 2
</div>
<div>
<heading>
this is heading 2 again
</heading>
this is a text for heading 2 again
</div>
</div>
</body>
Any help will be appreciated. Currenlty i have done this in asp.net but want to convert this into xslt.

Here is an XSLT 2.0 stylesheet:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf"
version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:function name="mf:group" as="node()*">
<xsl:param name="nodes" as="node()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:param name="max-level" as="xs:integer"/>
<xsl:choose>
<xsl:when test="$level le $max-level">
<xsl:for-each-group select="$nodes" group-starting-with="*[local-name() eq concat('h', $level)]">
<xsl:choose>
<xsl:when test="self::*[local-name() eq concat('h', $level)]">
<div>
<xsl:apply-templates select="."/>
<xsl:sequence select="mf:group(current-group() except ., $level + 1, $max-level)"/>
</div>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$nodes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[h1]">
<xsl:copy>
<xsl:sequence select="mf:group(node(), 1, 3)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="h1 | h2 | h3">
<heading>
<xsl:apply-templates/>
</heading>
</xsl:template>
</xsl:stylesheet>
When applied with Saxon 9.3 to the input
<body>
<h1>
heading 1
</h1>
this is a text for heading 1
This is a link
<h2>
this is heading 2
</h2>
this is a text for heading 2
<h2>
this is heading 2 again
</h2>
this is a text for heading 2 again
</body>
I get the following output
<body>
<div>
<heading>
heading 1
</heading>
this is a text for heading 1
This is a link
<div>
<heading>
this is heading 2
</heading>
this is a text for heading 2
</div>
<div>
<heading>
this is heading 2 again
</heading>
this is a text for heading 2 again
</div>
</div>
</body>
I haven't tested the XSLT with any other more complex input so test yourself and report back if you encounter any problems.

Related

How does parent tag Attributes in the "apply templete" when it is in "group by"

Here is the Sample XML which I am using
XML.xml
<root>
<main>
<docum year="2022" month="2022.01">1</docum>
<docum year="2021" month="2021.12">2</docum>
<docum year="2020" month="2020.11">3</docum>
<docum year="2020" month="2020.12">4</docum>
<docum year="2022" month="2022.12">5</docum>
</main>
</root>
Need a navigator HTML which is toc.htm and 1.htm, 2.htm, 3.htm
toc.htm
<div>
<div>2022_2022.01</div>
<div>2022_2022.12</div>
<div>2022_2020.11</div>
</div>
1.htm
<div>
<h1>2022</h1>
<h2>2022.01</h2>
1
</div>
<div>
<h1>2022</h1>
<h2>2022.12</h2>
5
</div>
2.htm
<div>
<h1>2021</h1>
<h2>2021.12</h2>
2
</div>
3.htm
<div>
<h1>2020</h1>
<h2>2020.11</h2>
3
</div>
<div>
<h1>2020</h1>
<h2>2020.11</h2>
4
</div>
this is my XSL which I am trying but not sure about how we get parent div attributes value in the apply template in the current group().
XSL.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:template match="root">
<xsl:apply-templates select="main" mode="toc" />
<xsl:apply-templates select="main" mode="chapter" />
</xsl:template>
<xsl:template match="root/main" mode="toc">
<xsl:result-document href="toc.htm" method="html">
<html>
<body>
<xsl:for-each select="docum">
<xsl:element name="div">
<xsl:attribute name="class"></xsl:attribute>
<xsl:attribute name="id">
<xsl:value-of select="#year" />
</xsl:attribute>
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="lower-case(concat('id_', generate-id(), '.htm'))" />
</xsl:attribute>
<xsl:value-of select="#year" /> _<xsl:value-of select="#month" />
</xsl:element>
</xsl:element>
</xsl:for-each>
</body>
</html>
</xsl:result-document>
</xsl:template>
<xsl:template match="root/main" mode="chapter">
<xsl:for-each-group select="docum" group-by="#month">
<xsl:result-document href="{lower-case(concat('id_', generate-id()))}.htm" method="HTML">
<html>
<body>
<h1><xsl:value-of select="#year" /></h1>
<h2><xsl:value-of select="#month" /></h2>
<xsl:apply-templates select="current-group()" />
</body>
</html>
</xsl:result-document>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
I think you want something along the following lines:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:template match="root/main">
<xsl:result-document href="toc.html">
<html>
<head>
<title>TOC</title>
</head>
<body>
<xsl:for-each-group select="docum" group-by="#year">
<div id="g-{current-grouping-key()}">
{let $sorted-months := sort(current-group()/#month, (), function($m) { $m }) return $sorted-months[1] || ('-' || $sorted-months[last()])[$sorted-months[2]]}
</div>
<xsl:result-document href="year-{current-grouping-key()}.html">
<html>
<head>
<title>{current-grouping-key()}</title>
</head>
<body>
<xsl:apply-templates select="current-group()"/>
</body>
</html>
</xsl:result-document>
</xsl:for-each-group>
</body>
</html>
</xsl:result-document>
</xsl:template>
<xsl:template match="docum">
<div>
<h1>{#year}</h1>
<h2>{#month}</h2>
{.}
</div>
</xsl:template>
</xsl:stylesheet>
This creates toc.html and for each year a year-yyyy.html which I think then has the contents you want, i.e. each month belonging to that year.

How do I add .01 to the end of a number using XSLT?

I need to add .01 to the entry in this line of code in my XSLT script <xsl:apply-templates select="descendant::xhtml:span[#property = 'atom:content-item-name']"/>. This number appears twice in my output. I only need the .01 on the second entry. How would I do that? I guess it would be in effect a counter because if there are 2 questions I'd need it to number as .02, etc.
This is the full XSLT script:
<?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:math="http://exslt.org/math"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:mml="http://www.w3.org/1998/Math/MathML"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="xs math xd xhtml mml"
version="3.0">
<xsl:output encoding="UTF-8" include-content-type="no" indent="no" method="xhtml" omit-xml-declaration="yes"/>
<!-- attributes, commments, processing instructions, text: copy as is -->
<xsl:template match="#*|comment()|processing-instruction()|text()">
<xsl:copy-of select="."/>
</xsl:template>
<!-- elements: create a new element with the same name, but no namespace -->
<xsl:template match="*">
<xsl:param name="content-item-name"/>
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="content-item-name" select="$content-item-name"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<!-- process root -->
<xsl:template match="xhtml:html">
<xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text>
<xsl:sequence select="'
'"/>
<html>
<xsl:apply-templates select="#* | node()"/>
</html>
</xsl:template>
<!-- process item, pass content item name variable -->
<xsl:template match="xhtml:li[#property='ktp:question']">
<li>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="content-item-name" select="descendant::xhtml:span[#property='atom:content-item-name']/#data-value"/>
</xsl:apply-templates>
</li>
</xsl:template>
<!-- branching point for any interaction type-based updates -->
<xsl:template match="xhtml:li[#property='ktp:question']">
<li>
<xsl:apply-templates select="#*|node()">
<xsl:with-param name="interactionType" select="//xhtml:span[#property='ktp:interactionType']"/>
</xsl:apply-templates>
</li>
</xsl:template>
<!-- Change content -->
<xsl:template match="xhtml:ol[#class='ktp-question-set']">
<xsl:variable name="content-item-name" select="xhtml:span[#property='atom:content-item-name']/#data-value"/>
<xsl:variable name="feedbackPara"
select="xhtml:section[#property = 'ktp:stimulus']"/>
<ol property="ktp:questionSet" typeof="ktp:QuestionSet" class="ktp-question-set">
<li class="ktp-question-set-meta">
<section property="ktp:metadata" class="ktp-meta">
<xsl:apply-templates
select="descendant::xhtml:span[#property = 'atom:content-item-name']"/>
</section>
<section property="ktp:tags" class="ktp-meta">
<span class="ktp-meta" property="ktp:questionSetType">shared-stimulus</span>
</section>
</li>
<li class="ktp-stimulus" typeof="ktp:Stimulus" property="ktp:stimulus">
<xsl:apply-templates
select="descendant::xhtml:section[#property = 'ktp:stimulus']"/>
<p class="place-top atom-exclude">Stimulus End: Place content above this line</p>
</li>
<li class="ktp-question" typeof="ktp:Question" property="ktp:question">
<section class="ktp-question-meta">
<section property="ktp:metadata" class="ktp-meta">
<xsl:apply-templates
select="descendant::xhtml:span[#property = 'atom:content-item-name']"/>
</section>
<xsl:apply-templates
select="descendant::xhtml:section[#property = 'ktp:tags']"/>
</section>
<xsl:apply-templates
select="descendant::xhtml:section[#class = 'ktp-question-stem']"/>
<xsl:apply-templates
select="descendant::xhtml:ol[#class = 'ktp-answer-set']"/>
<xsl:apply-templates
select="descendant::xhtml:section[#property = 'ktp:explanation']"/>
</li>
</ol>
</xsl:template>
</xsl:stylesheet>
This is the input section where I need to pull the data-value from:
<section class="ktp-question-meta">
<section property="ktp:metadata" class="ktp-meta"><span property="atom:content-item-name" class="ktp-meta" data-value="lsac820402"></span></section>
<section property="ktp:tags" class="ktp-meta">
<span property="ktp:interactionType" class="ktp-meta">single-select</span>
<span property="ktp:questionType" class="ktp-meta">Assumption (Sufficient)</span>
<span property="ktp:difficulty" class="ktp-meta">★</span>
</section>
This is how I need that section to appear with the .01 added to that data-value:
<section class="ktp-question-meta">
<section property="ktp:metadata" class="ktp-meta">
<span property="atom:content-item-name" class="ktp-meta" data-value="lsac820402.01"></span>
</section>

xsl:for-each-group not working as per exception

I have to transform XML tag out of p as separate div but i am unable to do it:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<p>
This is <bold>First</bold> paragraph
<boxed-text>
<sec>
<title>Boxed title</title>
<p>
<list list-type="bullet">
<list-item>
<p>List first para</p>
</list-item>
</list>
</p>
</sec>
</boxed-text>
This is <bold>Second</bold> paragraph
</p>
</root>
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"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xhtml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<html>
<head>
<title>Title</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="p">
<xsl:choose>
<xsl:when test="descendant::boxed-text">
<xsl:for-each-group select="node()" group-starting-with="node()">
<xsl:choose>
<xsl:when test="self::boxed-text">
<div class="boxed-text">
<xsl:apply-templates select="current-group()"/>
</div>
</xsl:when>
<xsl:otherwise>
<p class="indent">
<xsl:apply-templates select="current-group()"/>
</p>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<p class="indent">
<xsl:apply-templates/>
</p>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="boxed-text">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="p[table-wrap | list]">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="list">
<xsl:choose>
<xsl:when test="#list-type[. = 'bullet']">
<ol style="list-style-type:disc;">
<xsl:apply-templates/>
</ol>
</xsl:when>
<xsl:otherwise>
<ol style="list-style-type:none;">
<xsl:apply-templates/>
</ol>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="list-item">
<li>
<xsl:apply-templates/>
</li>
</xsl:template>
</xsl:stylesheet>
Current Output:
<?xml version="1.0" encoding="UTF-8"?><html>
<head>
<title>Title</title>
</head>
<body>
<p class="indent">
This is
</p>
<p class="indent">First</p>
<p class="indent"> paragraph
</p>
<div class="boxed-text">Boxed title
<ol style="list-style-type:disc;">
<li>
<p class="indent">List first para</p>
</li>
</ol>
</div>
<p class="indent">
This is
</p>
<p class="indent">Second</p>
<p class="indent"> paragraph
</p>
</body>
</html>
Excepted output:
<?xml version="1.0" encoding="UTF-8"?><html>
<head>
<title>Title</title>
</head>
<body>
<p class="indent">This is <b>First</b> paragraph</p>
<div class="boxed-text">Boxed title
<ol style="list-style-type:disc;">
<li>
<p class="indent">List first para</p>
</li>
</ol>
</div>
<p class="indent">This is <b>Second</b> paragraph</p>
</body>
</html>
It seems this is rather a job for group-adjacent="boolean(self::boxed-text)":
<xsl:template match="p[descendant::boxed-text]">
<xsl:for-each-group select="node()" group-adjacent="boolean(self::boxed-text)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<p class="indent">
<xsl:apply-templates select="current-group()"/>
</p>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
Complete adaption is
<?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"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="html" indent="no" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="p">
<p class="indent">
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="p[descendant::boxed-text]">
<xsl:for-each-group select="node()" group-adjacent="boolean(self::boxed-text)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<p class="indent">
<xsl:apply-templates select="current-group()"/>
</p>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="boxed-text">
<div class="boxed-text">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="p[table-wrap | list]">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="list">
<ol style="list-style-type:none;">
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="list[#list-type[. = 'bullet']]">
<ol style="list-style-type:disc;">
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="list-item">
<li>
<xsl:apply-templates/>
</li>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6rewNyg

XPath: Appending text only to the last text() occurrence (regardless of how deeply nested) while copying all XML.

Using XSLT on an XML file, my goal is to copy all XML while appending a snippet of text ("(PDF)" in this example) to only the last bit of text present in a particular tag regardless of how deeply nested that next is. I've managed to take care of most of the edge cases and gotten it close, but there's still one instance that's giving me trouble. I'm sure there's also a way to do this more efficiently so any tips are appreciated.
XML
<links>
This is a PDF file
<a href="something.PDF">
<span>
<b>This</b> is a PDF file
</span>
</a>
<a href="something.pdF">
<div>
<span>
This is a PDF file
</span>
</div>
</a>
<a href="something.pdf">
<div class="something">
<span>
This is a <i>PDF</i> file
</span>
</div>
</a>
<a href="something.pDf">
<div class="something">
<div>
<div>
Test Text
<div>
This is a <i>PDF</i>
</div>
</div>
</div>
</div>
</a>
</links>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="xml"/>
<xsl:strip-space elements="*" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:param name="pdf-append" select="'(PDF)'"/>
<xsl:template match="a['.pdf' = substring(translate(#href,'PDF','pdf'), string-length(#href) - 3)]/text()">
<xsl:value-of select="concat(current(),' ', $pdf-append)"/>
</xsl:template>
<xsl:template match="a['.pdf' = substring(translate(#href,'PDF','pdf'), string-length(#href) - 3)]//node()[last()]/text()[last()]">
<xsl:value-of select="concat(current(),' ', $pdf-append)"/>
</xsl:template>
</xsl:stylesheet>
Current Result
<links>
This is a PDF file (PDF)
<a href="something.PDF">
<span>
<b>This</b> is a PDF file
(PDF)</span>
</a>
<a href="something.pdF">
<div>
<span>
This is a PDF file
(PDF)</span>
</div>
</a>
<a href="something.pdf">
<div class="something">
<span>
This is a <i>PDF</i> file
(PDF)</span>
</div>
</a>
<a href="something.pDf">
<div class="something">
<div>
<div>
Test Text
(PDF)<div>
This is a (PDF)<i>PDF (PDF)</i>
</div>
</div>
</div>
</div>
</a>
</links>
The last <a> is the problem: ideally "(PDF)" would only appear within the the last <i> tag (e.g. This is a <i>PDF (PDF)</i>). So the questions is: how can I fix that last instance?
Thanks.
How about:
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:param name="pdf-append" select="' (PDF)'"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="."/>
<xsl:variable name="a" select="ancestor::a" />
<xsl:variable name="href" select="$a/#href" />
<xsl:variable name="extension" select="substring(translate($href,'PDF','pdf'), string-length($href) - 3)" />
<xsl:variable name="last-text" select="$a/descendant::text()[last()]" />
<xsl:if test="$extension='.pdf' and generate-id()=generate-id($last-text) ">
<xsl:value-of select="$pdf-append"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

How can i output this html with xsl( I want to exit an element)

I have this xsl bellow :
<xsl:template match="/" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:SharePoint="Microsoft.SharePoint.WebControls">
<div id="vtab">
<ul>
<xsl:call-template name="dvt_1.body"/>
</ul>
</div>
</xsl:template>
<xsl:template name="dvt_1.body">
<xsl:for-each select="/dsQueryResponse/Rows/Row[generate-id(.)=generate-id(key('TitleKey', #Title))]">
<li>
<xsl:value-of select="#Title"/>
</li>
<xsl:variable name="thisClassification" select="#Title" />
<div>
<xsl:for-each select="../Row[#Title = $thisClassification]">
<xsl:value-of select="#Model"/>
</xsl:for-each>
</div>
</xsl:for-each>
</xsl:template>
I would like have this output:
<div id="vtab">
<ul>
<li>HTC</li>
<li>Nokia</li>
</ul>
<div>HTC Sensation 345-HTC Ev</div>
<div>Nokia</div>
</div>
But now this is what i'm getting
<div id="vtab">
<ul>
<li>HTC</li>
<div>HTC Sensation 345HTC Evo</div>
<li>Nokia</li>
<div>Nokia</div>
</ul>
</div>
How can i exit the UL element and start the DIV element.
Thanks alot
It is not optimal, but consider making two loops something like the following:
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<div id="vtab">
<xsl:call-template name="LOOP_1"/>
<xsl:call-template name="LOOP_2"/>
</div>
</xsl:template>
<xsl:template name="LOOP_1">
<ul>
<xsl:for-each select="/dsQueryResponse/Rows/Row[generate-id(.)=generate-id(key('TitleKey', #Title))]">
<li>
<xsl:value-of select="#Title"/>
</li>
<xsl:variable name="thisClassification" select="#Title"/>
<div>
<xsl:for-each select="../Row[#Title = $thisClassification]">
<xsl:value-of select="#Model"/>
</xsl:for-each>
</div>
</xsl:for-each>
</ul>
</xsl:template>
<xsl:template name="LOOP_2">
<div>
<xsl:for-each select="/dsQueryResponse/Rows/Row[generate-id(.)=generate-id(key('TitleKey', #Title))]">
<xsl:variable name="thisClassification" select="#Title"/>
<div>
<xsl:for-each select="../Row[#Title = $thisClassification]">
<xsl:value-of select="#Model"/>
</xsl:for-each>
</div>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
I am not sure of your requirements, but it may be more efficient to review the desired output, and output nested lists and format it using CSS rather than creating separate loops