Choose from XML node with multiple category elements - xslt

I have an XML document with a collection of news articles that have been assigned to multiple categories. I now need to group these documents by category. Below is a sample item record:
<item>
<link>http://www.threelanews.com/articles/?id=50456</link>
<category>/General/</category>
<category>/Technology/</category>
<category>/Technology/Telecommunications/</category>
<category>/Technology/Information Technology/Internet/</category>
<title>Sony debuts handsets at CES</title>
<description>The Xperia S will be available globally from the first quarter of 2012.
Sony Ericsson will showcase the first handsets from...</description>
<pubDate>Tue, 10 Jan 2012 12:11:01 +0200</pubDate>
</item>
I am using an XSL stylesheet to transform the document. For each group, I will do a test to see what elements belong to that category and if there is a match in one of the category elements, the article should be added to the html eg:
<xsl:when test="contains(category,'/General/')">
<div class="news-item" width="100%">
<div class="news-item-title" width="100%">
<a href="{$linkUrl}" target="_blank">
<xsl:value-of select="title"/>
</a>
</div>
<xsl:if test="string-length($imageUrl) > 0">
<div class="news-item-image">
<img src="{$imageUrl}" />
</div>
</xsl:if>
<div class="news-item-description">
<xsl:value-of select="description"/>
</div>
</div>
<div class="clear" />
</xsl:when>
Then the article should be added to the "General Group". Articles with multiple categories should appear in each group that they are relevant. The above statement will work for the "General" group but when I try to do the same for "Technology" or the other categories below it, this article is not returned. I have found it is only doing the match on the first element. Is there any way I couuld do the match on all the category elements?

Try the following transforms:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Matched Items</h2>
<xsl:apply-templates select=
"//item[category/.='/General/']"/>
</body>
</html>
</xsl:template>
<xsl:template match="item">
<div class="news-item" width="100%">
<div class="news-item-title" width="100%">
<a href="{linkUrl}" target="_blank">
<xsl:value-of select="title"/>
</a>
</div>
<xsl:if test="string-length(imageUrl) > 0">
<div class="news-item-image">
<img src="{imageUrl}" />
</div>
</xsl:if>
<div class="news-item-description">
<xsl:value-of select="description"/>
</div>
</div>
<div class="clear" />
</xsl:template>
</xsl:stylesheet>
The important part is this XPath, which selects all item nodes that have a category with the given value:
//item[category/.='/General/']
Applying the transform to this document:
<items>
<item>
<link>http://www.threelanews.com/articles/?id=50456</link>
<category>/General/</category>
<category>/Technology/</category>
<category>/Technology/Telecommunications/</category>
<category>/Technology/Information Technology/Internet/</category>
<title>Sony debuts handsets at CES</title>
<description>The Xperia S will be available globally from the first quarter of 2012.
Sony Ericsson will showcase the first handsets from...</description>
<pubDate>Tue, 10 Jan 2012 12:11:01 +0200</pubDate>
</item>
<item>
<link>http://www.threelanews.com/articles/?id=50456</link>
<category>/Technology/</category>
<category>/Technology/Telecommunications/</category>
<category>/Technology/Information Technology/Internet/</category>
<title>Sony debuts handsets at CES</title>
<description>The Xperia S will be available globally from the first quarter of 2012.
Sony Ericsson will showcase the first handsets from...</description>
<pubDate>Tue, 10 Jan 2012 12:11:01 +0200</pubDate>
</item>
</items>
Gives the expected result:
<H2>Matched Items</H2>
<DIV class=news-item width="100%">
<DIV class=news-item-title width="100%"><A href="" target=_blank>Sony debuts handsets at CES</A></DIV>
<DIV class=news-item-description>The Xperia S will be available globally from the first quarter of 2012. Sony Ericsson will showcase the first handsets from...</DIV>
</DIV>
<DIV class=clear></DIV>

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:key name="kCategoryByVal" match="category"
use="substring-before(substring(.,2), '/')"/>
<xsl:template match=
"category
[generate-id()
=
generate-id(key('kCategoryByVal',
substring-before(substring(.,2), '/')
)
[1]
)
]
">
<xsl:variable name="vMainCat" select=
"substring-before(substring(.,2), '/')"/>
<h1><xsl:value-of select="$vMainCat"/></h1>
<xsl:apply-templates mode="inGroup" select=
"/*/item[category[starts-with(., concat('/', $vMainCat))]]"/>
</xsl:template>
<xsl:template match="item" mode="inGroup">
<div class="news-item" width="100%">
<div class="news-item-title" width="100%">
<a href="{link}" target="_blank">
<xsl:value-of select="title"/>
</a>
</div>
<xsl:apply-templates select="image[#url]" mode="inGroup"/>
<div class="news-item-description">
<xsl:value-of select="description"/>
</div>
</div>
<div class="clear" />
</xsl:template>
<xsl:template match="image" mode="inGroup">
<div class="news-item-image">
<img src="{#url}" />
</div>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on this XML document (derived from the provided, but made a little-bit more realistic):
<items>
<item>
<link>http://www.threelanews.com/articles/?id=50456</link>
<category>/General/</category>
<category>/Technology/</category>
<category>/Technology/Telecommunications/</category>
<category>/Technology/Information Technology/Internet/</category>
<title>Sony debuts handsets at CES</title>
<image url="http://www.blogcdn.com/www.engadget.com/media/2011/03/11x0328mar0424.jpg"/>
<description>The Xperia S will be available globally from the first quarter of 2012.
Sony Ericsson will showcase the first handsets from...</description>
<pubDate>Tue, 10 Jan 2012 12:11:01 +0200</pubDate>
</item>
<item>
<link>http://www.threelanews.com/articles/?id=50456</link>
<category>/Technology/</category>
<category>/Technology/Telecommunications/</category>
<category>/Technology/Information Technology/Internet/</category>
<title>Toshiba produces the next Portege</title>
<description>The next Portege will be available globally from the first quarter of 2012.
Sony Ericsson will showcase the first handsets from...</description>
<pubDate>Tue, 10 Jan 2012 12:11:01 +0200</pubDate>
</item>
</items>
the wanted result is produced (all different categories and the items in them):
<h1>General</h1>
<div class="news-item" width="100%">
<div class="news-item-title" width="100%">
Sony debuts handsets at CES
</div>
<div class="news-item-image">
<img src="http://www.blogcdn.com/www.engadget.com/media/2011/03/11x0328mar0424.jpg"/>
</div>
<div class="news-item-description">The Xperia S will be available globally from the first quarter of 2012.
Sony Ericsson will showcase the first handsets from...</div>
</div>
<div class="clear"/>
<h1>Technology</h1>
<div class="news-item" width="100%">
<div class="news-item-title" width="100%">
Sony debuts handsets at CES
</div>
<div class="news-item-image">
<img src="http://www.blogcdn.com/www.engadget.com/media/2011/03/11x0328mar0424.jpg"/>
</div>
<div class="news-item-description">The Xperia S will be available globally from the first quarter of 2012.
Sony Ericsson will showcase the first handsets from...</div>
</div>
<div class="clear"/>
<div class="news-item" width="100%">
<div class="news-item-title" width="100%">
Toshiba produces the next Portege
</div>
<div class="news-item-description">The next Portege will be available globally from the first quarter of 2012.
Sony Ericsson will showcase the first handsets from...</div>
</div>
<div class="clear"/>
Explanation:
All different main categories are found using Muenchian grouping.
For each of the main categories, all item elements that have a category with string value that makes the item in this main category, the item's data is output appropriately formatted.
In this solution an assumption is made that only "Main" categories (the starting category-name in a string that contains a category and subcategories) need be listed.

Related

xslt not transforming xml correctly in browser

I have the following XML:
<item>
<title>Testing WebForm</title>
<link>http://linkurlhere.com</link>
<description>
<div class="field field-name-body field-type-text-with-summary
field-label-hidden"><div class="field-items"><div class="field-item even"
property="content:encoded"><div style="background-color: white; width: 100%;">
<div id="prize" style="background-color: yellow; color: #660000; font-weight:
bold; width: 200px;">Prize here</div>
</div>
<div id="startDate">Start Date: January 1, 2013</div>
<div id="endDate">End Date: January 1, 2014</div>
<p></p>
<p>Thanks for playing please take the survey - mock intro</p>
</div></div></div></description>
</item>
And I am rendering it with the following XSLT:
<xsl:template match="rss/channel">
<div id="surveyList">
<p> Please
click on the survey below and complete the survey.</p>
<xsl:for-each select="item">
<div id="surveyItem">
<div id="thePrize">
<xsl:apply-templates select="description" />
</div>
<a id="surveyLink">
<xsl:attribute name="href">
<xsl:value-of select="link"
disable-output-escaping="yes" />
</xsl:attribute>
<xsl:attribute name="target">_blank</xsl:attribute>
<xsl:attribute name="title">Click to complete this survey</xsl:attribute>
<xsl:value-of select="title" disable-output-escaping="yes" />
</a>
<br />
</div>
</xsl:for-each>
</div>
</xsl:template>
<xsl:template match="rss/channel/item/description" name="description">
<xsl:value-of select=".//div[#id='prize']" disable-output-escaping="yes" />
</xsl:template>
When I run this through an IDE such as Altova, and transform it, it shows up correctly with the div prize text. However, when I build out the webapp and deploy it either locally on tomcat 6.0 or on an actual webserver the prize does not show up, and it is not in the source when I view the page.
I have tried multiple browsers to no avail. Is this a simple setup problem with my XSLT page?
Thanks.
Updated below with complete XML including RSS elements:
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xml:base="http://drupal-survey.url" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>drupal-survey.url</title>
<link>http://drupal-survey.url</link>
<description></description>
<language>en</language>
<item>
<title>Testing WebForm</title>
<link>urlLink here</link>
<description>
<div class="field field-name-body field-type-text-with-summary field-label-hidden">
<div class="field-items"><div class="field-item even" property="content:encoded">
<div style="background-color: white; width: 100%;">
<div id="prize" style="background-color: yellow; color: #660000; font-weight: bold; width: 200px;">$1000 Gift Card to McDonald's</div>
</div>
<div id="startDate">Start Date: January 1, 2013</div>
<div id="endDate">End Date: January 1, 2014</div>
<p>Thanks for playing please take the survey - mock intro</p>
</div></div></div></description>
<pubDate>Mon, 25 Feb 2013 19:12:48 +0000</pubDate>
<dc:creator>admin</dc:creator>
<guid isPermaLink="false">6 at http://drupal-survey.url</guid>
</item>
<item>
<title>sss162 test</title>
<link>http://drupal-survey.url/sss162/test</link>
<description><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>This is a testing paragraph</p>
<p>this is also</p>
</div></div></div></description>
<pubDate>Thu, 10 May 2012 19:24:43 +0000</pubDate>
<dc:creator>sss162</dc:creator>
<guid isPermaLink="false">4 at http://drupal-survey.url</guid>
</item>
<item>
<title>Orientation Survey</title>
<link>http://drupal-survey.url</link>
<description><div class="field field-name-body field-type-text-with-summary field-label-hidden">
<div class="field-items"><div class="field-item even" property="content:encoded"><div id="startDate">Start Date: January 1, 2013</div>
<div id="endDate">End Date: January 1, 2014</div>
<p>This may be the brief explanation paragraph.</p>
</div></div></div></description>
<pubDate>Thu, 10 May 2012 17:11:38 +0000</pubDate>
<dc:creator>admin</dc:creator>
<guid isPermaLink="false">2 at http://drupal-survey.url</guid>
</item>
</channel>
</rss>

XSL: Print divider by checking variables

I'm new to XSL and i want to do something like
var oldvalue= ''
for each
get currentvalue
if (oldvalue != currentvalue)
{
print divider
oldvalue = currentvalue
}
end for
I've tried it with
<xsl:variable name="oldname" select="name" />
<xsl:for-each select="myxpathstring">
<xsl:choose>
<xsl:when test="$newname = $newname">
<xsl:variable name="oldname" select="$newname" />
<div class='divider'>divider stuff </div>
</xsl:when>
<xsl:otherwise>No</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
But that doesn't work because i can't update the 'oldname' variable.
Anyone have a solution ?
The complete XSL (with JSP-parameters because i generate the XSL dynamically)
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="counter" select="count(<%= request.getParameter("xpath")%>)" />
<div id='counter' class='ui-state-highlight ui-corner-all'><p><span style="float: left; margin-right: .3em;" class="ui-icon ui-icon-info"></span><xsl:value-of select="$counter"/> schema's gevonden</p></div>
<div class="page_navigation ui-widget-header ui-corner-all"></div>
<ul id='schemeslist' class="content">
<xsl:for-each select="<%= request.getParameter("xpath")%>"><!-- filter on sports //scheme[ (sports/sport ='Fietsen') and (planduration=12 or planduration=16)]-->
<xsl:sort select="name"/>
<!-- ////////////////// -->
<!-- print divider if name is new -->
<!-- ////////////////// -->
<li class='scheme' id='scheme'>
<div class='schemeSports'>
<!-- <xsl:for-each select="sports/sport">
<xsl:value-of select="."/>
</xsl:for-each> -->
<xsl:value-of select="sport"/>
</div>
<xsl:variable name="theid" select="#id" />
<div class='schemeName'><xsl:value-of select="name"/></div>
<div class='planDuration'><xsl:value-of select="planduration"/></div>
<div class='fitnessLevel'><xsl:value-of select="fitnesslevel"/></div>
<div class='order'>
<xsl:if test="price != ''"><xsl:value-of select="price"/>euro</xsl:if>
<button class='more' onClick='showInfo("{$theid}")' id='{$theid}'>MEER</button> <button class='buy' onClick='window.location = "buy.html?ID={$theid}"'>KOOP</button>
</div>
</li>
</xsl:for-each>
</ul>
<div style='clear:both'></div>
<div class="page_navigation ui-widget-header ui-corner-all"></div>
</xsl:template>
</xsl:stylesheet>
and here is a sample of the complete xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<schemes lang='nl-BE'>
<scheme id='5E47B7E9'>
<sport>Fietsen</sport>
<author>Energy Lab</author>
<name>Voorbereiding op de Ronde van Vlaanderen</name>
<planduration>16</planduration>
<fitnesslevel>Beginner</fitnesslevel>
<frequency>1-3</frequency>
<longtraining></longtraining>
<rollers>Ja</rollers>
<price>12</price>
<description>
<![CDATA[...]]>
</description>
</scheme>
<scheme id='5E47B7E9'>
<sport>Triatlon</sport>
<author>Energy Lab</author>
<name>Voorbereiding op de Ronde van Vlaanderen</name>
<planduration>16</planduration>
<fitnesslevel>Expert</fitnesslevel>
<frequency>1-3</frequency>
<longtraining></longtraining>
<rollers>Ja</rollers>
<price>12</price>
<description>
<![CDATA[...]]>
</description>
</scheme>
<scheme id='5E47B7E9'>
<sport>Fietsen</sport>
<author>Energy Lab</author>
<name>Voorbereiding op een Triatlon</name>
<planduration>24</planduration>
<fitnesslevel>Beginner</fitnesslevel>
<frequency>1-3</frequency>
<longtraining></longtraining>
<rollers>Ja</rollers>
<price>48</price>
<description>
<![CDATA[...]]>
</description>
</scheme>
</schemes>
Create a function that returns the new value, taking as parameters the old value and the list of values in myxpathstring.
If I understand your XSLT correct you have to use muenchian grouping. Read this article for a description of how this method works.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<schemes lang='nl-BE'>
<scheme id='5E47B7E9'>
<sport>Fietsen</sport>
<author>Energy Lab</author>
<name>Voorbereiding op de Ronde van Vlaanderen</name>
<planduration>16</planduration>
<fitnesslevel>Beginner</fitnesslevel>
<frequency>1-3</frequency>
<longtraining></longtraining>
<rollers>Ja</rollers>
<price>12</price>
<description><![CDATA[...]]></description>
</scheme>
<scheme id='5E47B7E9'>
<sport>Triatlon</sport>
<author>Energy Lab</author>
<name>Voorbereiding op de Ronde van Vlaanderen</name>
<planduration>16</planduration>
<fitnesslevel>Expert</fitnesslevel>
<frequency>1-3</frequency>
<longtraining></longtraining>
<rollers>Ja</rollers>
<price>12</price>
<description><![CDATA[...]]></description>
</scheme>
<scheme id='5E47B7E9'>
<sport>Fietsen</sport>
<author>Energy Lab</author>
<name>Voorbereiding op een Triatlon</name>
<planduration>24</planduration>
<fitnesslevel>Beginner</fitnesslevel>
<frequency>1-3</frequency>
<longtraining></longtraining>
<rollers>Ja</rollers>
<price>48</price>
<description><![CDATA[...]]></description>
</scheme>
</schemes>
XSLT:
I replaced the first jsp-tag with a dot and removed the second because I have no idea what you try to achieve with. I do not recommend dynamic *.xsl. You better put these information in your input.xml and query it with xpath.
<?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' indent='yes' omit-xml-declaration='yes'/>
<xsl:key name='schemata' match='scheme' use='name'/>
<xsl:template match='scheme'>
<li class='scheme' id='scheme'>
<div class='schemeSports'>
<xsl:value-of select="sport"/>
</div>
<xsl:variable name="theid" select="#id"/>
<div class='schemeName'>
<xsl:value-of select="name"/>
</div>
<div class='planDuration'>
<xsl:value-of select="planduration"/>
</div>
<div class='fitnessLevel'>
<xsl:value-of select="fitnesslevel"/>
</div>
<div class='order'>
<xsl:if test="price != ''">
<xsl:value-of select="price"/>
euro
</xsl:if>
<button class='more' onClick='showInfo("{$theid}")' id='{$theid}'>MEER</button>
<button class='buy' onClick='window.location = "buy.html?ID={$theid}"'>KOOP</button>
</div>
</li>
</xsl:template>
<xsl:template match='/schemes'>
<xsl:variable name="counter" select="count(.)"/> <!-- jsp -->
<div id='counter' class='ui-state-highlight ui-corner-all'>
<p>
<span style="float: left; margin-right: .3em;" class="ui-icon ui-icon-info"/>
<xsl:value-of select="$counter"/>
<xsl:text> schema's gevonden</xsl:text>
</p>
</div>
<div class="page_navigation ui-widget-header ui-corner-all"/>
<ul id='schemeslist' class="content">
<!-- muenchian grouping -->
<xsl:for-each select='scheme[generate-id() = generate-id(key("schemata", name)[1])]'>
<xsl:sort select='name'/>
<xsl:if test='position() != 1'>
<div class='divider'/>
</xsl:if>
<xsl:apply-templates select='key("schemata", name)'/>
</xsl:for-each>
</ul>
<div style='clear:both'/>
<div class="page_navigation ui-widget-header ui-corner-all"/>
</xsl:template>
</xsl:stylesheet>
Result:
<div id="counter" class="ui-state-highlight ui-corner-all">
<p><span style="float: left; margin-right: .3em;" class="ui-icon ui-icon-info"/>1 schema's gevonden</p>
</div><div class="page_navigation ui-widget-header ui-corner-all"/><ul id="schemeslist" class="content">
<li class="scheme" id="scheme">
<div class="schemeSports">Fietsen</div>
<div class="schemeName">Voorbereiding op de Ronde van Vlaanderen</div>
<div class="planDuration">16</div>
<div class="fitnessLevel">Beginner</div>
<div class="order">12
euro
<button class="more" onClick="showInfo("5E47B7E9")" id="5E47B7E9">MEER</button><button class="buy" onClick="window.location = "buy.html?ID=5E47B7E9"">KOOP</button></div>
</li>
<li class="scheme" id="scheme">
<div class="schemeSports">Triatlon</div>
<div class="schemeName">Voorbereiding op de Ronde van Vlaanderen</div>
<div class="planDuration">16</div>
<div class="fitnessLevel">Expert</div>
<div class="order">12
euro
<button class="more" onClick="showInfo("5E47B7E9")" id="5E47B7E9">MEER</button><button class="buy" onClick="window.location = "buy.html?ID=5E47B7E9"">KOOP</button></div>
</li>
<div class="divider"/>
<li class="scheme" id="scheme">
<div class="schemeSports">Fietsen</div>
<div class="schemeName">Voorbereiding op een Triatlon</div>
<div class="planDuration">24</div>
<div class="fitnessLevel">Beginner</div>
<div class="order">48
euro
<button class="more" onClick="showInfo("5E47B7E9")" id="5E47B7E9">MEER</button><button class="buy" onClick="window.location = "buy.html?ID=5E47B7E9"">KOOP</button></div>
</li>
</ul><div style="clear:both"/><div class="page_navigation ui-widget-header ui-corner-all"/>

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 1.0 Grouping on multiple values on multiple levels

edited in response to comments *
Hello,
I am an XSLT noob and need some help. I am trying to do an filter/group combination with XSLT 1.0 (can't use XSLT 2.0 for this application).
Here is an example of the xml
<entry>
<item>
<name>Widget 2</name>
<rank>2</rank>
<types>
<type>Wood</type>
<type>Fixed</type>
<type>Old</type>
</types>
</item>
<item>
<name>Widget 1</name>
<rank>2</rank>
<types>
<type>Metal</type>
<type>Broken</type>
<type>Old</type>
</types>
</item>
<item>
<name>Widget 3</name>
<rank>1</rank>
<types>
<type>Metal</type>
<type>New</type>
</types>
</item>
</entry>
Now what I want to do is output html where I get a subset of the XML based on <type> and then group on rank. For example, if the user selects all items with the type Metal, the output should be:
<p class="nospace"><font color="#800000">
<b>Rank 1</b></font></p>
<li id="mylist"><b>Widget 3</b></li>
<br\>
<p class="nospace"><font color="#800000">
<b>Rank 2</b></font></p>
<li id="mylist"><b>Widget 1</b></li>
<br\>
of if the user user chooses the type Old the output would be
<p class="nospace"><font color="#800000">
<b>Rank 2</b></font></p>
<li id="mylist"><b>Widget 1</b></li>
<li id="mylist"><b>Widget 2</b></li>
<br\>
I can group using keys on rank along easily enough, but trying to do both is not working. Here is a sample of the xslt I have tried:
<xsl:param name="typeParam"/>
<xsl:key name="byRank" use="rank" match="item"/>
<xsl:for-each select="item[count(.|key('byRank',rank)[1])=1]">
<xsl:sort data-type="number" select="rank"/>
<xsl:for-each select="key('byRank',rank)">
<xsl:sort select="name"/>
<xsl:if test="count(rank)>0">
<p class="nospace"><font color="#800000"><b>Rank<xsl:value-of select="rank"/></b></font></p>
<xsl:for-each select="types[types=$typeParam]">
<li id="mylist"><b><xsl:value-of select="../name"/></b></li>
</xsl:for-each>
<br/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
The result I get from this is I do indeed get the subset of my xml that I want but it also displays all of the various rank values. I want to limit it to just the ranks of the type that is specified in $typeParam.
I have tried moving the for-each statement to earlier in the code as well as modifying the if statement to select for $typeParam but neither works. I have also tried concat-ing my key with rank and type but that doesn't seem to work either (It only works if the type in $typeParam is the first child under types).
Thanks
jeff
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kItemByRank" match="item" use="rank"/>
<xsl:param name="pType" select="'Old'"/>
<xsl:template match="entry">
<xsl:for-each select="item[count(.|key('kItemByRank',rank)[1])=1]">
<xsl:sort select="rank" data-type="number"/>
<xsl:variable name="vGroup" select="key('kItemByRank',rank)[
types/type = $pType
]"/>
<xsl:if test="$vGroup">
<p class="nospace">
<font color="#800000">
<b>
<xsl:value-of select="concat('Rank ',rank)"/>
</b>
</font>
</p>
<xsl:apply-templates select="$vGroup">
<xsl:sort select="name"/>
</xsl:apply-templates>
<br/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="item">
<li id="mylist">
<b>
<xsl:value-of select="name"/>
</b>
</li>
</xsl:template>
</xsl:stylesheet>
Output:
<p class="nospace">
<font color="#800000">
<b>Rank 1</b>
</font>
</p>
<li id="mylist">
<b>Widget 3</b>
</li>
<br />
<p class="nospace">
<font color="#800000">
<b>Rank 2</b>
</font>
</p>
<li id="mylist">
<b>Widget 1</b>
</li>
<br />
And whit pType param set to 'Old', output:
<p class="nospace">
<font color="#800000">
<b>Rank 2</b>
</font>
</p>
<li id="mylist">
<b>Widget 1</b>
</li>
<li id="mylist">
<b>Widget 2</b>
</li>
<br />

XSLT: trying to do a contains() with a full XPath. Why does this fail?

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" />
<xsl:template match="/">
<html>
<body>
<span>
<div style="background-color:#000066;color:#EEEEEE;padding:7px">
<a name="top" style="padding-left:10px;font-size:28pt">Alerting Variables</a>
</div>
</span>
<div style="display:block;padding-left:50px;padding-bottom:10px" class="hbuttons">
LDMS Alerts
LDSM Alerts
Why?
Examples
Resources
</div>
<div style="clear: left;"></div>
<!-- This is the Table of Contents-->
<div style="padding:5px">
<div style="padding:5px;margin-top:10pt;margin-bottom:10pt;font-weight:bold;font-size:20px">Table of Contents -
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintAll.html">
<img border="0" src="images/PrintButton.png" />
</a></div>
<div style="font-family:Arial;font-weight:bold;margin-left:30px;font-size:10pt">
<xsl:if test="contains(identifiers/sectionname/alert/#name, 'Agent Watcher')">
Agent Watcher
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintAW.html">
<img border="0" src="images/PrintButton.png" />
</a>
</xsl:if>
<ol style="margin-top:5">
<xsl:for-each select="identifiers/sectionname/alert">
<xsl:if test="contains(#name, 'Agent Watcher')">
<li style="margin-left:10pt;font-size:8pt">
<a>
<xsl:attribute name="href">#
<xsl:value-of select="#name" /></xsl:attribute>
<xsl:value-of select="#name" />
</a>
</li>
</xsl:if>
</xsl:for-each>
</ol>
</div>
<div style="font-family:Arial;font-weight:bold;margin-left:30px;font-size:10pt">
Intel vPro
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintvPro.html">
<img border="0" src="images/PrintButton.png" />
</a>
<ol style="margin-top:5">
<xsl:for-each select="identifiers/SectionName/alert">
<xsl:if test="contains(#name, 'Intel vPro')">
<li style="margin-left:10pt;font-size:8pt">
<a>
<xsl:attribute name="href">#
<xsl:value-of select="#name" /></xsl:attribute>
<xsl:value-of select="#name" />
</a>
</li>
</xsl:if>
</xsl:for-each>
</ol>
</div>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Above is my code sample.
The first xsl:if statement always fails and never shows the Agent Watcher text or print me button. Even if the section is filled out in the XML. If the section is there, the first xsl:if statement fails, but the second one, contained in the xsl:for-each shows the content. How do I get this to work.
I want to have it encompassing so that if the XML has content in the section it will put it up but if not it wont be empty content with a header or vice versa. Attaching sample XML to process.
<identifiers>
<sectionname>
<alert name="Agent Watcher Service Startup"></alert>
<alert name="Agent Watcher Service Not Started"></alert>
<alert name="Agent Watcher Service Uninstalled"></alert>
<alert name="Agent Watcher File Deleted"></alert>
</sectionname>
<sectionname>
<alert name="Intel vPro agentless discovery failure"></alert>
<alert name="Intel vPro System Defense Remediation Alert"></alert>
<alert name="Intel vPro Enhanced System Defense Remediation Alert"></alert>
<alert name="Intel vPro Enhanced System Defense Alert"></alert>
</sectionname>
</identifiers>
Blockquote
I have a few other suggestions but you need to post the entire (relevant) XSLT before I can go on. At least the enclosing template is necessary.
EDIT: Here is my proposal for your stylesheet:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tmp="http://tempuri.org"
exclude-result-prefixes="tmp"
>
<tmp:config>
<tmp:alert label="Agent Watcher" link="PrintPages/PrintAW.html" />
<tmp:alert label="Intel vPro" link="PrintPages/PrintvPro.html" />
</tmp:config>
<xsl:variable name="everyAlert" select="
/identifiers/sectionname/alert
" />
<xsl:template match="/">
<html>
<body>
<!-- 8< snip -->
<div style="...">
<div style="...">
<xsl:text>Table of Contents - </xsl:text>
<a style="..." href="PrintPages/PrintAll.html">
<img border="0" src="images/PrintButton.png" />
</a>
</div>
<xsl:for-each select="document('')/*/tmp:config/tmp:alert">
<xsl:call-template name="section" />
</xsl:for-each>
</div>
</body>
</html>
</xsl:template>
<xsl:template name="section">
<xsl:variable name="this" select="." />
<xsl:variable name="alerts" select="
$everyAlert[contains(#name, $this/#label)]
" />
<xsl:if test="$alerts">
<div style="...">
<a href="#{translate($this/#label, ' ', '_')}">
<xsl:value-of select="$this/#label" />
</a>
<a style="..." href="{$this/#link}">
<img border="0" src="images/PrintButton.png" />
</a>
<ol style="...">
<xsl:for-each select="$alerts">
<li style="...">
<xsl:value-of select="#name" />
</li>
</xsl:for-each>
</ol>
</div>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Key features:
efficient code reuse through a named template
printed sections are easily configurable
uses <xsl:text> elements to avoid unwanted whitespace in the output while retaining freedom to format the XSLT code properly
uses attribute literal notation (the curly braces {}) instead of verbose <xsl:attribute> elements
uses a temporary namespace to allow storing config data in the stylesheet itself
uses an <xsl:for-each> loop and the document() function to retrieve and work with that config data
the for-each makes use of the context to transport the current #label and #link so no <xsl:param> is necessary (the <xsl:template name="section"> runs in tmp:config/tmp:alert context, not in sectionname/alert context!)
uses a global variable ($everyAlert) to store all nodes for later use