XSLT passing variables inside nested for-each loops - xslt

I am fairly new at this and I am not sure if it is possible to do what I want to achieve here. I have 3 for-each loops that stores a number for every chapter, section and paragraph. I want to get that stored number from the previous for-each loop and display it in the nested loop, but I can't get it to work.
<?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>
<head>
<title>Table of Contents, Chapter 3</title>
</head>
<body>
<xsl:for-each select="chapter">
<tr>
<xsl:variable name="capnr">
<xsl:number value="3" format="1. "/>
</xsl:variable>
<xsl:value-of select="$capnr"/>
<xsl:value-of select="#title"/>
</tr>
<br />
<xsl:for-each select="section">
<tr>
<xsl:variable name="secnr">
<xsl:number format="1. "/>
</xsl:variable>
<xsl:value-of select="$capnr"/>
<xsl:value-of select="$secnr"/>
<xsl:value-of select="#title"/>
</tr>
<br />
<xsl:for-each select="paragraph">
<tr>
<xsl:variable name="parnr">
<xsl:number format="1 "/>
</xsl:variable>
<xsl:value-of select="$capnr"/>
<xsl:value-of select="$secnr"/>
<xsl:value-of select="$parnr"/>
<xsl:value-of select="#title"/>
</tr>
</xsl:for-each>
<br />
</xsl:for-each>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
XML:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="ToC-3.xsl"?>
<chapter title="Chapter 3: Expressions">
<section title="Variables">
<paragraph title="Simple variables"></paragraph>
<paragraph title="Text variables"></paragraph>
<paragraph title="Remote identifiers"></paragraph>
</section>
<section title="The logical operators">
<paragraph title="Precedence of Boolean operators"></paragraph>
</section>
<section title="Designational expressions">
</section>
</chapter>

Without more details on the input and output, this should work. Of note is that the variable name is on the root, to allow all templates to access it. but since there appears to be one XSl per chapter this shouldn't be a problem.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="capnr">
<xsl:number value="3" format="1."/>
</xsl:variable>
<xsl:template match="/">
<html>
<head>
<title>Table of Contents, Chapter 3</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="chapter">
<h1>
<xsl:value-of select="$capnr"/>
<xsl:value-of select="#title"/>
</h1>
<ol><xsl:apply-templates/></ol>
</xsl:template>
<xsl:template match="section">
<li>
<xsl:value-of select="$capnr"/>
<xsl:value-of select="position()"/>
<xsl:text> - </xsl:text>
<xsl:value-of select="#title"/>
<ol><xsl:apply-templates/></ol>
</li>
</xsl:template>
<xsl:template match="paragraph">
<li>
<xsl:value-of select="$capnr"/>
<xsl:value-of select="count(../preceding-sibling::*) + 1"/>
<xsl:text>.</xsl:text>
<xsl:value-of select="position()"/>
<xsl:text> - </xsl:text>
<xsl:value-of select="#title"/>
<ol><xsl:apply-templates/></ol>
</li>
</xsl:template>
</xsl:stylesheet>
The above when applied to the given input XMl gives:
<html>
<head>
<title>Table of Contents, Chapter 3</title>
</head>
<body>
<h1>3.Chapter 3: Expressions</h1>
<ol><li>3.1 - Variables<ol><li>3.1.1 - Simple variables<ol></ol>
</li>
<li>3.1.2 - Text variables<ol></ol>
</li>
<li>3.1.3 - Remote identifiers<ol></ol>
</li>
</ol>
</li>
<li>3.2 - The logical operators<ol><li>3.2.1 - Precedence of Boolean operators<ol></ol>
</li>
</ol>
</li>
<li>3.3 - Designational expressions<ol></ol>
</li>
</ol>
</body>
</html>

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.

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

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

Xslt extracting attributes and generating html

I'm trying to transform the following XML..
<?xml version="1.0" encoding="utf-16"?><Member TextRank="unknown" FullName="My Name" ..etc.. />
Into something like the following,
<div class="member">
<span class="label">
Text Rank (note: i want to override the labels in the xslt)
</div>
<span class="value">
unknown
</span>
<span class="label">
Full Name
</div>
<span class="value">
My Name
</span>
..etc..
</div>
How if possible could I do this using xslt?
Here's a different approach, that does away for the need for the xsl:choose element. Instead, you could take advantage of the matching templates to have specific templates for the cases of attributes who names you want to override, and a generic template for other case.
To avoid repetition of code, you could also make the generic template a named template, with a parameter to override the name
<xsl:template match="#*" name="attribute">
<xsl:param name="label" select="local-name()" />
So, the default for most attributes would be to use the attribute name, but the specific template for #FullName could then call this with a different name.
<xsl:template match="#FullName">
<xsl:call-template name="attribute">
<xsl:with-param name="label" select="'Full Name'" />
</xsl:call-template>
</xsl:template>
Here is the full XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="*">
<div class="{local-name()}">
<div> Title: </div>
<xsl:apply-templates select="#*"/>
</div>
</xsl:template>
<xsl:template match="#FullName">
<xsl:call-template name="attribute">
<xsl:with-param name="label" select="'Full Name'" />
</xsl:call-template>
</xsl:template>
<xsl:template match="#*" name="attribute">
<xsl:param name="label" select="local-name()" />
<span class="label">
<xsl:value-of select="concat($label, ' : ')"/>
</span>
<span class="value">
<xsl:value-of select="."/>
</span>
<br/>
</xsl:template>
</xsl:stylesheet>
When applied to the following XML:
<Member TextRank="unknown" ID="12" FullName="My Name" Dob="01/01/1970" />
The following is output:
<div class="Member">
<div> Title: </div>
<span class="label">TextRank : </span>
<span class="value">unknown</span>
<br>
<span class="label">ID : </span>
<span class="value">12</span>
<br>
<span class="label">Full Name : </span>
<span class="value">My Name</span>
<br>
<span class="label">Dob : </span>
<span class="value">01/01/1970</span>
<br>
</div>
This is the solution I came up with.
<?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" />
<xsl:template match="*">
<xsl:element name="div">
<xsl:attribute name="class">className</xsl:attribute>
<div>
Title:
</div>
<!-- UID, Name, DBO-->
<xsl:apply-templates select="#ID"/>
<xsl:apply-templates select="#FullName"/>
<xsl:apply-templates select="#Dob"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:element name="span">
<xsl:attribute name="class">label</xsl:attribute>
<xsl:choose>
<xsl:when test="name() = 'FullName'">
Full Name
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name()"/>
</xsl:otherwise>
</xsl:choose>
:
</xsl:element>
<xsl:element name="span">
<xsl:attribute name="class">value</xsl:attribute>
<xsl:value-of select="."/>
</xsl:element>
<br/>
</xsl:template>
</xsl:stylesheet>

XSLT for-each failing to pick up fields

I've got a curious item, I'm trying to enumerate through an xml file with my XSLT transform, however I'm after putting a for-each loop into the code my records are not pulling any details (anymore). Help!
XSLT
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="pathid" select="montage/path" />
<xsl:variable name="outputstr" select="''" />
<xsl:template match="vista">
<xsl:variable name="pathid" select="path" />
<html>
<body>
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" width="800" height="200">
<xsl:apply-templates select="item" />
</table>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="item">
<xsl:variable name="monid" select="mon" />
<!--<xsl:value-of select="format-number(position(), '#')" />-->
<xsl:variable name="outputstr" select="file" />
<xsl:choose>
<xsl:when test="position() >= 0 and 4 > position()">
<tr height="200">
<xsl:for-each select="item">
<td width="157" height="108" valign="top">
<xsl:attribute name="height">
<xsl:value-of select="'200'"/>
</xsl:attribute>
<a>
<xsl:attribute name="href">
<xsl:value-of select="$pathid" />
<xsl:value-of select="thepage" />
</xsl:attribute>
<img>
<xsl:attribute name="src">
<xsl:value-of select="concat(substring-before($outputstr,'abc'),$pathid)"/>
<xsl:value-of select="$outputstr" />
</xsl:attribute>
</img>
</a>
</td>
</xsl:for-each>
</tr>
</xsl:when>
</xsl:choose>
<xsl:attribute name="id">mon</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
XML
<?xml version="1.0"?>
<vista>
<lastupdate>14/12/2010 14:32</lastupdate>
<path>C:\</path>
<item>
<thepage>00000657.html</thepage>
<heading>heading1</heading>
<file>test123.png</file>
<description>df bhdf hdfhdf hdfh he rher herh df bdf bdfb df rfbd bd</description>
</item>
<item>
<thepage>00000660.html</thepage>
<heading>heading2</heading>
<file>test456.jpg</file>
<description>reh erh erherh erh erher herh</description>
</item>
</vista>
Outputted HTML
<html>
<body>
<div align="center">
<table border="0" cellpadding="0" cellspacing="0" width="800" height="200">
<tr height="200"></tr>
<tr height="200"></tr>
<tr height="200"></tr>
<tr height="100"></tr>
<tr height="100"></tr>
<tr height="100"></tr>
<tr height="100"></tr>
</table>
</div>
</body>
</html>
The line <xsl:for-each select="item"> - I think you're already in "item". You can't select it again.
You already have an item template - why do you need to for-each inside it?
I expect this to fail, as item nodes do not contain item nodes.