XSLT - How to evaluate a single node in multiple places - xslt

I am building an image rotator in XSLT that requires the following markup:
<div class="wrapper">
<div class="overlay"></div>
<div id="slider" class="slider">
[IMAGE FROM NODE A GOES HERE]
[IMAGE FROM NODE B GOES HERE]
...
</div>
<div id="htmlcaption" class="html-caption">
[CAPTION FOR NODE A GOES HERE]
[CAPTION FOR NODE B GOES HERE]
...
</div>
</div>
I need help constructing the XSLT so that Node A would get evaluated inside #slider then get re-evaluated in #htmlcaption, then Node B, and so on.
Any help would be greatly appreciated.
Thanks!

First, it is perfectly possible to evaluate a source element multiple times. Just use the same selector.
For example, considering the following XML:
<images>
<node id="a" image="foo.png" caption="foo" />
<node id="b" image="bar.png" caption="bar" />
</images>
This XSLT will repeatedly output stuff from the first node:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="images">
<div id="images">
<img><xsl:value-of select="node[#id='a']/#image"/></img>
<img><xsl:value-of select="node[#id='a']/#caption"/></img>
<img><xsl:value-of select="node[#id='a']/#image"/></img>
<img><xsl:value-of select="node[#id='a']/#caption"/></img>
</div>
</xsl:template>
Output:
<div id="images">
<img>foo.png</img>
<img>foo</img>
<img>foo.png</img>
<img>foo</img>
</div>
However it looks like what you really want is to loop over a bunch of nodes containing image and caption.
You could use a for-each loop to avoid selecting the nodes by name:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="images">
<div id="images">
<xsl:for-each select="node">
<img><xsl:value-of select="#image"/></img>
</xsl:for-each>
</div>
<div id="captions">
<xsl:for-each select="node">
<div><xsl:value-of select="#caption"/></div>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
Which will produce:
<div id="images">
<img>foo.png</img>
<img>bar.png</img>
</div>
<div id="captions">
<div>foo</div>
<div>bar</div>
</div>

Related

XSLT for dynamic tree

You can see my xml patern sample below. Every manuel has chapter tags, chapter tags may have subchapter and checklist tags. There is no static depth, it may change document to document, so I couldnt write the dynamic recursive xslt code to generate Html. Does anyone knows, how can I achive this.
<manuel name="Test">
<chapter name="00">
<subchapter name="00.01">
<checklist name="00.01.01">
<summary>abc</summary>
</checklist>
</subchapter>
<subchapter name="00.02">
<checklist name="00.02.01">
<summary>def</summary>
</checklist>
<subchapter name="00.02.02">
<checklist name="00.02.02.01">
<summary>xyz</summary>
</checklist>
</subchapter>
</subchapter>
<checklist name="00.03">
<summary>ZZZZ</summary>
</checklist>
</chapter>
</manuel>
For this sample, I suppose this result. I can set the css style, its not important now. The problem is generating the structure.
<div class="cssChapter"> 00</div>
<div class="cssSubChapter"> 00.01 </div>
<div class="cssCheckList"> 00.01.01 </div>
<div class="cssSummary"> abc </div>
<div class="cssSubChapter"> 00.02 </div>
<div class="cssCheckList"> 00.02.01 </div>
<div class="cssSummary"> def </div>
<div class="cssSubChapter"> 00.02.02 </div>
<div class="cssCheckList"> 00.02.02.01 </div>
<div class="cssSummary"> xyz </div>
<div class="cssCheckList"> 00.03 </div>
<div class="cssSummary"> ZZZZ</div>
Use a simple apply-templates:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:template match="manuel">
<xsl:apply-templates select=".//*"/>
</xsl:template>
<xsl:template match="chapter">
<div class="cssChapter">
<xsl:value-of select="#name"/>
</div>
</xsl:template>
<xsl:template match="subchapter">
<div class="cssSubChapter">
<xsl:value-of select="#name"/>
</div>
</xsl:template>
<xsl:template match="checklist">
<div class="cssChecklist">
<xsl:value-of select="#name"/>
</div>
</xsl:template>
<xsl:template match="summary">
<div class="cssSummary">
<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:transform>
Online at http://xsltransform.net/3NJ3915/1.

xslt self match child node but with different html div id

I am transforming xml to html using saxon.
I have xml like this:
<abc>
level-1
<abc>
level-2
<abc>
level-x
</abc>
</abc>
</abc>
in html i want to do something like this:
<div class="abc-1">
level-1
<div class="abc-2">
level-2
<div class="abc-3">
level-3
</div>
</div>
</div>
Now in my xslt i want to do somthing like following, so i can have different class names in child node with same node match, but not sure what could be good and optimized way to do this in xslt.
<xsl:template match = "abc">
<div class="abc<x>">
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template match="abc">
<div class="abc-{count(ancestor-or-self::abc)}">
<xsl:apply-templates />
</div>
</xsl:template>

XSLT For-Each Wrapping every nth item in a div

I have a series of nodes that are direct child nodes to a parent I want to loop over those nodes but have them wrapped in 'groups' of 4... I'm probably not wording this very clearly so this might help;
<span class="child01">#nodename</span>
<span class="child02">#nodename</span>
<span class="child03">#nodename</span>
<span class="child04">#nodename</span>
<span class="child05">#nodename</span>
<span class="child06">#nodename</span>
<span class="child07">#nodename</span>
<span class="child08">#nodename</span>
..
<span class="child32">#nodename</span>
<span class="child33">#nodename</span>
..and so on
Goal
<div class="group">
<span class="child01">#nodename</span>
<span class="child02">#nodename</span>
<span class="child03">#nodename</span>
<span class="child04">#nodename</span>
</div>
<div class="group">
<span class="child05">#nodename</span>
<span class="child06">#nodename</span>
<span class="child07">#nodename</span>
<span class="child08">#nodename</span>
</div>
<div class="group">
..
<span class="child32">#nodename</span>
</div>
<div class="group">
<span class="child33">#nodename</span>
..and so on
I have tried variations on this idea - wrapping the lot in the open and closing group tags and every fourth loop drop in a new close / open pair
<div class="group">
<xsl:for-each select="$currentPage/*">
<span>
<xsl:value-of select="#nodeName" />
</span>
<!--
=============================================================
After very 4th item
=============================================================
-->
<xsl:if test="position() mod 4 = 0">
<xsl:text></div><div class="page"></xsl:text>
</xsl:if>
</xsl:for-each>
</div>
But essentially it seems XSLT won't let me start with a closing unmatched tag
The clkoset solution I ahve found so far is a 'fix' in jquery Wrapping a div around every three divs but I would rather not rely on javascript to format the page.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pNumCols" select="3"/>
<xsl:template match="/*">
<xsl:apply-templates select="span[position() mod $pNumCols = 1]"/>
</xsl:template>
<xsl:template match="span">
<div>
<xsl:copy-of select=
".|following-sibling::span[not(position() > $pNumCols -1)]"/>
</div>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<t>
<span class="child01">#nodename</span>
<span class="child02">#nodename</span>
<span class="child03">#nodename</span>
<span class="child04">#nodename</span>
<span class="child05">#nodename</span>
<span class="child06">#nodename</span>
<span class="child07">#nodename</span>
<span class="child08">#nodename</span> ..
<span class="child32">#nodename</span>
<span class="child33">#nodename</span>
</t>
produces the wanted result:
<div>
<span class="child01">#nodename</span>
<span class="child02">#nodename</span>
<span class="child03">#nodename</span>
</div>
<div>
<span class="child04">#nodename</span>
<span class="child05">#nodename</span>
<span class="child06">#nodename</span>
</div>
<div>
<span class="child07">#nodename</span>
<span class="child08">#nodename</span>
<span class="child32">#nodename</span>
</div>
<div>
<span class="child33">#nodename</span>
</div>
If like me you need to transform the source elements that are being divided by position, use xsl:for-each instead of xsl:copy:
<xsl:template match="span">
<ol>
<xsl:for-each select=".|following-sibling::span[not(position() > $pNumCols -1)]"/>
<li><xsl:value-of select="./text()"/></li>
</xsl:for-each>
</ol>
</xsl:template>
Faced by the same problem, that is wanting to output
<div class="container">
<div class="row">
<div class="col">...</div>
<div class="col"/>...</div>
</div>
<div class="row">
...
</div>
</div>
from a CXML (Collection XML) file (http://gallery.clipflair.net/collection/activities.cxml - the data behind the PivotViewer display at http://gallery.clipflair.net/activity)
I coined up the following, based on other suggestions here, but using "mode" attribute of "template" and "apply-templates" XSL tags instead which make it cleaner I believe:
<?xml version="1.0" encoding="UTF-8"?>
<?altova_samplexml http://gallery.clipflair.net/collection/activities.cxml?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cxml="http://schemas.microsoft.com/collection/metadata/2009"
exclude-result-prefixes="cxml"
>
<xsl:output method="html" version="4.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="COLUMNS" select="2"/>
<!-- ########################### -->
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>ClipFlair Activities</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<!-- ########################### -->
<xsl:template match="cxml:Collection">
<div class="container">
<xsl:apply-templates/>
</div>
</xsl:template>
<!-- ########################### -->
<xsl:template match="cxml:Items">
<xsl:apply-templates select="cxml:Item[position() mod $COLUMNS = 1]" mode="row"/>
</xsl:template>
<!-- ########################### -->
<xsl:template match="cxml:Item" mode="row">
<div class="row">
<div>----------</div>
<xsl:apply-templates select=".|following-sibling::cxml:Item[position() < $COLUMNS]" mode="col"/>
</div>
</xsl:template>
<xsl:template match="cxml:Item" mode="col">
<xsl:variable name="URL" select="#Href"/>
<xsl:variable name="FILENAME" select="cxml:Facets/cxml:Facet[#Name='Filename']/cxml:String/#Value"/>
<div class="col">
<xsl:value-of select="$FILENAME"/> --- <xsl:value-of select="$URL"/>
</div>
</xsl:template>
<!-- ########################### -->
<xsl:template match="*">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()|#*">
</xsl:template>
</xsl:stylesheet>
the output from the above when run in Altova XMLSpy tool (note that it uses altova_samplexml processor instruction to find the XML data) is:
2DaysInParis-OpenActivity-CapRev-FR-EN.clipflair --- http://studio.clipflair.net/?activity=2DaysInParis-OpenActivity-CapRev-FR-EN.clipflair
Abu_Dukhan-CapRev-A1-AR.clipflair --- http://studio.clipflair.net/?activity=Abu_Dukhan-CapRev-A1-AR.clipflair
----------
AFarewellToArms-RevCap-C2-EN.clipflair --- http://studio.clipflair.net/?activity=AFarewellToArms-RevCap-C2-EN.clipflair
agComhaireamhCountingRND.clipflair --- http://studio.clipflair.net/?activity=agComhaireamhCountingRND.clipflair
----------
Al-imtihan-CapRev-B1-AR.clipflair --- http://studio.clipflair.net/?activity=Al-imtihan-CapRev-B1-AR.clipflair
AlBar-Cap-B1-B2-IT.clipflair --- http://studio.clipflair.net/?activity=AlBar-Cap-B1-B2-IT.clipflair
...

XSLT replace tag by generated one

I'm quite new to XSLT.
This is the problem I'm trying to solve for hours now:
I auto-generate a table of contents for a xml document which works great so far. However I'd like to replace a placeholder tag in my source xml with that just generated toc code.
So the output should include the whole document with replaced placeholder-toc-tag with the auto-generated toc xml.
This is what I've tried:
Let's say I have my placeholderTag anywhere in the document and want to replace this/those. I thought that I could loop through all nodes by node() and check if the node name equals my placeholder tag:
<xsl:template match="node()">
<xsl:choose>
<xsl:when test="divGen">
<!-- apply other template to generate toc-->
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="node()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
However the if statement won't match like this.
edit:
Ok, here's the source document (TEI coded - TEI namespace removed):
<TEI>
<teiHeader>
<fileDesc>
<titleStmt>
<title>Title</title>
</titleStmt>
<publicationStmt>
<p>Publication information</p>
</publicationStmt>
<sourceDesc>
<p>Information about the source</p>
</sourceDesc>
</fileDesc>
</teiHeader>
<text>
<front>
<titlePage>
<byline>title page details</byline>
</titlePage>
</front>
<body>
<divGen type="toc"/>
<div type="part">
<div type="section">
<head>heading1</head>
</div>
<div type="section">
<head>heading2</head>
</div>
</div>
<div type="part">
<div type="section">
<head>heading3</head>
</div>
<div type="section">
<head>heading4</head>
</div>
<div type="section">
<head>heading5</head>
</div>
</div>
</body>
<back> </back>
</text>
I'd like to auto-generate the toc from the head values and replace the divGen tag by the auto-produced toc code. However please notice that the divGen tag can be anywhere in the document but not outside of body.
Any ideas?
Chris
Good question, +1.
Here is a complete transformation (with mock TOC generation to be replaced by real one) that shows how to do this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vTOC">
<xsl:apply-templates mode="TOC"/>
<mockTOC>
<xsl:comment>The real TOC generated here</xsl:comment>
</mockTOC>
</xsl:variable>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="divGen[#type='toc']">
<xsl:copy-of select="$vTOC"/>
</xsl:template>
<xsl:template match="text()" mode="TOC"/>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<TEI>
<teiHeader>
<fileDesc>
<titleStmt>
<title>Title</title>
</titleStmt>
<publicationStmt>
<p>Publication information</p>
</publicationStmt>
<sourceDesc>
<p>Information about the source</p>
</sourceDesc>
</fileDesc>
</teiHeader>
<text>
<front>
<titlePage>
<byline>title page details</byline>
</titlePage>
</front>
<body>
<divGen type="toc"/>
<div type="part">
<div type="section">
<head>heading1</head>
</div>
<div type="section">
<head>heading2</head>
</div>
</div>
<div type="part">
<div type="section">
<head>heading3</head>
</div>
<div type="section">
<head>heading4</head>
</div>
<div type="section">
<head>heading5</head>
</div>
</div>
</body>
<back>
</back>
</text>
</TEI>
the correct, wanted output is produced, in which any occurences of <divGen type="toc"/> are replaced by the generated TOC:
<TEI>
<teiHeader>
<fileDesc>
<titleStmt>
<title>Title</title>
</titleStmt>
<publicationStmt>
<p>Publication information</p>
</publicationStmt>
<sourceDesc>
<p>Information about the source</p>
</sourceDesc>
</fileDesc>
</teiHeader>
<text>
<front>
<titlePage>
<byline>title page details</byline>
</titlePage>
</front>
<body>
<mockTOC><!--The real TOC generated here--></mockTOC>
<div type="part">
<div type="section">
<head>heading1</head>
</div>
<div type="section">
<head>heading2</head>
</div>
</div>
<div type="part">
<div type="section">
<head>heading3</head>
</div>
<div type="section">
<head>heading4</head>
</div>
<div type="section">
<head>heading5</head>
</div>
</div>
</body>
<back/>
</text>
</TEI>
Explanation: Use of modes to pre-generate the TOC in a variable, then overriding the identity rule for any TOC placeholder.
Im guessing somewhere u have a template like
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
then u want the template to only match your placeholderTag
then the others will default to your other nodes!
<xsl:template match="placeholderTag">
<!-- applying generate toc thing-->
</xsl:template>
<xsl:template match="node()">
<xsl:copy-of select="node()"/>
</xsl:template>

XSLT to transform a XHTML document

I am new to XSLT but it was suggested to me as a way to accomplish a specific task. I have a bunch of xhtml files that I would like to remove a sidebar from. The sidebar is contained in a <div class="foo"> element.
I can successfully perform the identity transform using the instructions in this answer. But I can't seem to match the elements I am wanting to remove. Perhaps this because they are not top level elements like they are in every example of this design pattern I find?
Can someone explain the correct way to remove <div class="foo"> and all its children from the identity transform?
It is quite likely that your problem arises due to the presence of a default (xhtml) namespace in the source XHTML file (which you have not shown to us, so this is a guess at best).
Can someone explain the correct way to
remove and all its
children from the identity transform?
Here is how to do this in case a default namespace is present:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<xsl:output 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="xhtml:div[#class='foo']"/>
</xsl:stylesheet>
When this transformation is applied on the following XHTML document:
<html xmlns="http://www.w3.org/1999/xhtml">
<div class="class1">
<p>Text1</p>
</div>
<div class="foo">
<p>Text foo</p>
</div>
<div class="class2">
<p>Text2</p>
</div>
</html>
the wanted, correct result is produced:
<html xmlns="http://www.w3.org/1999/xhtml">
<div class="class1">
<p>Text1</p>
</div>
<div class="class2">
<p>Text2</p>
</div>
</html>
Using a namespace prefix in the match expression of the template is necessary, because XPath considers any unprefixed name in "no namespace" and a match expression with non-prefixed names does not match any nodes, because it specifies nodes in "no namspace", but all the nodes of the source document are in the XHTML namespace.
In case there is no default namespace in the source document, the transformation can be simplified:
When this transformation is applied on the following XML document (note that it doesn't define a default namespace):
<html>
<div class="class1">
<p>Text1</p>
</div>
<div class="foo">
<p>Text foo</p>
</div>
<div class="class2">
<p>Text2</p>
</div>
</html>
the wanted, correct result is produced:
<html>
<div class="class1">
<p>Text1</p>
</div>
<div class="class2">
<p>Text2</p>
</div>
</html>
Both transformation use the identity rule to copy any node of the document and another template, which overrides the identity rule for nodes matching "div[#class='foo']". This second template is empty (has no body), which means that the matched node and the subtree rooted in it are not processed at all (ignored) and thus will not appear in the output.