xslt select rss item by both child node and position - xslt

Using XSLT 1.0 with an RSS calendar feed, I want to exclude expired items - those with pubDates before the current date - then include only three current items. The result should be the next three future events. I used http://exslt.org/date/index.html to create a variable for the current system date. The problem is that when I select="item[not(position() > 4)] and substring(item/pubDate,5,11) &gt= $current", I end up with less than three items if any of the first ones are expired. Apparently my code selects three items then removes the expired ones, which is not what I want. Is it possible to save a copy of the unexpired items and then select three of them?
Since XSLT 1.0 doesn't provide inequality string comparison operators, I may not be able to see if a value such as "30 Oct 2013" is greater than "29 Oct 2013," I can format the values as 30102013 and 29102013 instead, but it still seems that I'm trying to concatenate channel/item/pubDate before I have selected it. So knowing how to process the XML/RSS in two stages, if possible, would be helpful.
I have tried several techniques, with similar results:
<xsl:for-each select="substring(item/pubDate,5,11) >= $current and item[not(position() > 4)]">
<xsl:template match="item[not(position() > 4)]">
<xsl:apply-templates select="item"/>
<xsl:for-each select="substring(item/pubDate,5,11) >= $current">
<xsl:if test=" item[not(position() > 4)]">
Sample XML:
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<atom:link href="http://calendar.example.edu/" rel="self" type="application/rss+xml"/>
<title>University Calendar - Featured Events</title>
<link>http://calendar.example.edu/</link>
<description>List of featured events on calendar.example.edu</description>
<language>en-us</language>
<pubDate>Tue, 27 Oct 2013 20:47:05 CDT</pubDate>
<item>
<title>Creative Movement Program Student Show</title>
<description/>
<link>http://calendar.example.edu/?&y=2013&m=10&d=30&eventdatetime_id=19721</link>
<guid>calendar.example.edu/?&y=2013&m=10&d=30&eventdatetime_id=19721</guid>
<pubDate>Wed 30 Oct 2013, 17:00:00 CDT</pubDate>
</item>
<item>
<title>Philosophy Career Fair</title>
<description>The Department of Philosophy brings recruiters from around the state to interview seniors and alumni.</description>
<link>http://calendar.example.edu/?&y=2013&m=11&d=04&eventdatetime_id=16427</link>
<guid>calendar.example.edu/?&y=2013&m=11&d=04&eventdatetime_id=16427</guid>
<pubDate>Mon 04 Nov 2013, 07:00:00 CDT</pubDate>
</item>
<item>
<title>Football vs. Caltech</title>
<description/>
<link>http://calendar.example.edu/?&y=2013&m=12&d=07&eventdatetime_id=16521</link>
<guid>calendar.example.edu/?&y=2013&m=12&d=07&eventdatetime_id=16521</guid>
<pubDate>Sat 07 Dec 2013, 00:00:00 CDT</pubDate>
</item>
<item>
<title>Mural Exhibition</title>
<description>The College of Arts presents an overview of wall paintings from the Caves of Lascaux to the Kiev train station.</description>
<link>http://calendar.example.edu/?&y=2014&m=01&d=14&eventdatetime_id=16759</link>
<guid>calendar.example.edu/?&y=2014&m=01&d=14&eventdatetime_id=16759</guid>
<pubDate>Tue 14 Jan 2014, 07:00:00 CDT</pubDate>
</item>
</channel>
</rss>
Current XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" extension-element-prefixes="date" version="1.0" xmlns:date="http://exslt.org/dates-and-times" >
<xsl:template name="lf">
<xsl:text/>
</xsl:template>
<xsl:template match="rss">
<section id="campusEvents" role="region">
<h2 id="eventsTitle">
Campus Events
</h2>
<xsl:apply-templates select="channel"/>
<div class="moreLink">
Full Calendar
</div>
</section>
</xsl:template>
<xsl:template match="channel">
<xsl:variable name="currDay" select="substring(date:date(),9,2)"/>
<xsl:variable name="currMonth">
<xsl:call-template name="format-month">
<xsl:with-param name="date" select="date:date()"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="currYear" select="substring(date:date(),1,4)"/>
<xsl:variable name="current" select="concat($currDay,' ',$currMonth,' ',$currYear )"/>
<xsl:for-each select="item[not(position() > 4)] and substring(item/pubDate,5,11) >= $current">
<div class="eventBlock">
<xsl:call-template name="lf"/>
<div class="dateBlock">
<xsl:call-template name="lf"/>
<div class="eventMonth">
<xsl:value-of select="substring(pubDate,8,3)"/>
</div>
<div class="eventDate">
<xsl:value-of select="substring(pubDate,5,2)"/>
</div>
</div>
<xsl:call-template name="lf"/>
<div class="eventDescription">
<a class="url" href="{link}">
<xsl:value-of select="title"/>
</a>
<xsl:call-template name="lf"/>
</div>
<xsl:call-template name="lf"/>
</div>
<xsl:call-template name="lf"/>
</xsl:for-each>
</xsl:template>
<xsl:template name="format-month">
<xsl:param name="date"/>
<xsl:variable name="monthName" select="substring(date:date(),6,2)"/>
<xsl:variable name="month">
<xsl:choose>
<xsl:when test="$monthName = '01'">Jan</xsl:when>
<xsl:when test="$monthName = '02'">Feb</xsl:when>
<xsl:when test="$monthName = '03'">Mar</xsl:when>
<xsl:when test="$monthName = '04'">Apr</xsl:when>
<xsl:when test="$monthName = '05'">May</xsl:when>
<xsl:when test="$monthName = '06'">Jun</xsl:when>
<xsl:when test="$monthName = '07'">Jul</xsl:when>
<xsl:when test="$monthName = '08'">Aug</xsl:when>
<xsl:when test="$monthName = '09'">Sep</xsl:when>
<xsl:when test="$monthName = '10'">Oct</xsl:when>
<xsl:when test="$monthName = '11'">Nov</xsl:when>
<xsl:when test="$monthName = '12'">Dec</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$month"/>
</xsl:template>
</xsl:stylesheet>
Desired Result (after Oct. 30 event has expired):
<section role="region" id="campusEvents">
<h2 id="eventsTitle">
Campus Events
</h2>
<div class="eventBlock">
<div class="dateBlock">
<div class="eventMonth">Nov</div>
<div class="eventDate">04</div>
</div>
<div class="eventDescription">
Philosophy Career Fair
</div>
</div>
<div class="eventBlock">
<div class="dateBlock">
<div class="eventMonth">Dec</div>
<div class="eventDate">07</div>
</div>
<div class="eventDescription">
Football vs. Caltech
</div>
</div>
<div class="eventBlock">
<div class="dateBlock">
<div class="eventMonth">Jan</div>
<div class="eventDate">14</div>
</div>
<div class="eventDescription">
Mural Exhibition
</div>
</div>
<div class="moreLink">
Full Calendar
</div>
</section>

If you want to compare dates then you'll have to somehow massage the various date expressions into all-numeric yyyymmdd format (e.g. 20131029) so that the chronological ordering is equivalent to numerical ordering. For the current date that's a simple global variable:
<xsl:variable name="curDateStr" select="date:date()" />
<xsl:variable name="currentDateNum"
select="concat(substring($curDateStr, 1, 4),
substring($curDateStr, 6, 2),
substring($curDateStr, 9, 2))" />
and to parse the pubDate values I'd use a named template that's the reverse of your current format-month
<xsl:template name="parse-date">
<xsl:param name="dateStr" />
<xsl:value-of select="substring($dateStr, 12, 4)" /><!-- year -->
<xsl:variable name="month" select="substring($dateStr, 8, 3)" />
<xsl:choose>
<xsl:when test="$month = 'Jan'">01</xsl:when>
<xsl:when test="$month = 'Feb'">02</xsl:when>
<!-- etc. -->
</xsl:choose>
<xsl:value-of select="substring($dateStr, 5, 2)" /><!-- day -->
</xsl:template>
Now the main logic can be implemented using a tail-recursive template which is the nearest you can get in XSLT to a "while" loop:
<xsl:template match="item">
<xsl:param name="numItems" select="3" />
<xsl:if test="$numItems > 0"><!-- stop if we hit the threshold -->
<xsl:variable name="itemDate">
<xsl:call-template name="parse-date">
<xsl:with-param name="dateStr" select="pubDate" />
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$itemDate > $currentDateNum">
<!-- do what you need to do to produce output for this item -->
<!-- ..... -->
<xsl:apply-templates select="following-sibling::item[1]">
<!-- we processed this item, so decrement $numItems -->
<xsl:with-param name="numItems" select="$numItems - 1" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="following-sibling::item[1]">
<!-- we ignored this item, so don't decrement $numItems -->
<xsl:with-param name="numItems" select="$numItems" />
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
<xsl:if>
</xsl:template>
and then in the channel template you start this "loop" by applying templates to the first item only
<xsl:template match="channel">
<xsl:apply-templates select="item[1]" />
</xsl:template>
The item template will then keep processing siblings until either it runs out of item elements completely, or it has processed 3 items that meet the date criteria.

Related

split a number and create an orderedlist

<orderedlist type="manual">
<item num="8. (1)">
<para id="t2_HKWB-S2-1.1_pgfId-135">What is your present estimate of the length of trial?</para>
</item>
<item num="(2)">
<para id="t2_HKWB-S2-1.1_pgfId-136">What is the earliest date that you believe you can be ready for trial?</para>
</item>
</orderedlist>
This is a slight tricky one as here, first the number 8. (1) should be split. 8 should be in an orderedlist and then 1 and 2 should be in same orderedlist(nested), under the main orderedlist, please let me know how i can achieve this.
i'm already trying with an xsl and it is as below.
<xsl:template name="orderedlist" match="orderedlist">
<ol class="eng-orderedlist orderedlist">
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template name="orderitem" match="item">
<li class="item">
<xsl:apply-templates/>
</li>
</xsl:template>
<xsl:template name="orderitempara" match="item/para">
<div class="para">
<xsl:choose>
<xsl:when test="position()=1">
<span class="item-num">
<xsl:if test="position()=1">
<xsl:value-of select="parent::item[1]/#num"/>
</xsl:if>
</span>
<xsl:text> </xsl:text>
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:template>
But the output i get is like below:
8. (1)What is your present estimate of the length of trial?
(2)What is the earliest date that you believe you can be ready for trial?
should look something like this.
8 (1)What is your present estimate of the length of trial?
(2)What is the earliest date that you believe you can be ready for trial?
Thanks

XSLT 1.0 How to match a string to another, list it, and under a different heading, list the unmatched string (specific situation)

I have been training myself in XSLT for about 1.5 months. I have made a simplified shorter version of what I am having trouble figuring out, and would highly appreciate any help at all as I am stuck on the issue. Thanks!
Basic Situation:
There's a string in a root attribute with an ancestor of element definition
xpath:
/v3:QualityMeasureDocument/v3:component/v3:dataCriteriaSection/v3:definition/v3:valueSet/v3:id/#root
...that when matched with the id from a valueSet attribute with an ancestor of element entry, xpath:
/v3:QualityMeasureDocument/v3:component/v3:dataCriteriaSection/v3:entry/v3:observationCriteria/v3:value/#valueSet
or
/v3:QualityMeasureDocument/v3:component/v3:dataCriteriaSection/v3:entry/v3:observationCriteria/v3:code/#valueSet
the output (needs to and currently does) display the string, along with its required attribute(s).
However, when there is no match for the string in these locations, the string must also be listed, with a 'Not Specified' header.
THE ERROR:
The 'Not Specified' header and its string are being listed even when all of the existing strings match. In this scenario, there should only be matched strings listed.
The Problem Translator (XSL file) :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:v3="urn:hl7-org:v3"
xmlns:rvs="urn:ihe:iti:svs:2008">
<xsl:output method="html" standalone="yes" omit-xml-declaration="no" indent="yes" xalan:indent-amount="2"/>
<xsl:template
match="/v3:QualityMeasureDocument">
<html>
<head>
<title>Test 'I'</title>
</head>
<body>
<ul>
<xsl:apply-templates select="//v3:dataCriteriaSection" />
</ul>
</body>
</html>
</xsl:template>
<xsl:template
match="v3:dataCriteriaSection">
<xsl:for-each select="//v3:entry">
<xsl:if test="*/v3:value/#valueSet">
<xsl:call-template name="definitionValueSet">
<xsl:with-param name="cur_valueSetID" select="*/v3:value/#valueSet"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="*/v3:code/#valueSet">
<xsl:call-template name="definitionValueSet">
<xsl:with-param name="cur_valueSetID" select="*/v3:code/#valueSet"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="*/v3:participant/v3:roleParticipant/v3:code/#valueSet">
<xsl:call-template name="definitionValueSet">
<xsl:with-param name="cur_valueSetID" select="*/v3:participant/v3:roleParticipant/v3:code/#valueSet"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template
name="definitionValueSet">
<xsl:param name="cur_valueSetID"/>
<xsl:for-each select="//v3:valueSet">
<xsl:choose>
<xsl:when test="$cur_valueSetID != v3:id/#root or
not( v3:text/v3:reference[starts-with(#value, 'https://') and contains(#value, $cur_valueSetID)] ) or
not( v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/#id = $cur_valueSetID )">
<xsl:if test="not($cur_valueSetID = '')">
<li>
<xsl:text>Not Specified</xsl:text>
<ul>
<li>
<xsl:text>ValueSet: </xsl:text>
<xsl:value-of select="$cur_valueSetID"></xsl:value-of>
</li>
</ul>
</li>
</xsl:if>
</xsl:when>
<xsl:when test="v3:id/#root = $cur_valueSetID">
<xsl:if test="v3:text/v3:reference[starts-with(#value, 'https://') and contains(#value, $cur_valueSetID)]">
<xsl:if test="v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/#id = $cur_valueSetID">
<li>
<xsl:text>Id: </xsl:text>
<xsl:value-of select="v3:id/#root"/>
<xsl:for-each select="v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/rvs:ConceptList/rvs:Concept">
<ul>
<li>
<xsl:if test="not(#code = '')">
<xsl:if test="#code">
<xsl:text>code = </xsl:text>
<xsl:value-of select="#code"></xsl:value-of>
<xsl:text> </xsl:text>
</xsl:if>
</xsl:if>
</li>
</ul>
</xsl:for-each>
</li>
</xsl:if>
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XML input file 'I' xml :
<?xml version="1.0" encoding="UTF-8"?>
<QualityMeasureDocument xmlns="urn:hl7-org:v3">
<component>
<dataCriteriaSection>
<definition>
<valueSet>
<!-- Value Set for Race -->
<id root='1.1.1.1.1.1.1' />
<text>
<reference
value='https://localhost/RetrieveValueSet?id=1.1.1.1.1.1.1' />
<RetrieveValueSetResponse xmlns="urn:ihe:iti:svs:2008">
<ValueSet id="1.1.1.1.1.1.1">
<ConceptList>
<Concept code="4" />
<Concept code="5" />
<Concept code="6" />
</ConceptList>
</ValueSet>
</RetrieveValueSetResponse>
</text>
</valueSet>
</definition>
<definition>
<valueSet>
<id root='1.1.1.1.1.1.2' />
<text>
<reference
value='https://localhost/RetrieveValueSet?id=1.1.1.1.1.1.2' />
<RetrieveValueSetResponse xmlns="urn:ihe:iti:svs:2008">
<ValueSet id="1.1.1.1.1.1.2">
<ConceptList>
<Concept code="007.2" />
<Concept code="007.3" />
</ConceptList>
</ValueSet>
</RetrieveValueSetResponse>
</text>
</valueSet>
</definition>
<entry>
<observationCriteria>
<code code="424144002" codeSystem="123123213"
displayName="FEWFW" />
<value>
<low />
<high />
</value>
</observationCriteria>
</entry>
<entry>
<observationCriteria>
<code code="DFHKJ" codeSystem="ASKJDHK" displayName="ASDNJK" />
<value>
<width />
</value>
</observationCriteria>
</entry>
<entry>
<observationCriteria>
<code code="FDSFD" codeSystem="JHBHJB" displayName="HJGJH" />
<value valueSet="1.1.1.1.1.1.1" />
</observationCriteria>
</entry>
<entry>
<encounterCriteria>
<code valueSet="1.1.1.1.1.1.2" />
</encounterCriteria>
</entry>
</dataCriteriaSection>
</component>
</QualityMeasureDocument>
Expected Output Code 'I' HTML :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<html xmlns:xalan="http://xml.apache.org/xalan" xmlns:rvs="urn:ihe:iti:svs:2008" xmlns:v3="urn:hl7-org:v3">
<head>
<title>Test 'I'</title>
</head>
<body>
<ul>
<li>Id: 1.1.1.1.1.1.1
<ul>
<li>code = 4 </li>
</ul>
<ul>
<li>code = 5 </li>
</ul>
<ul>
<li>code = 6 </li>
</ul>
</li>
<li>Id: 1.1.1.1.1.1.2
<ul>
<li>code = 007.2 </li>
</ul>
<ul>
<li>code = 007.3 </li>
</ul>
</li>
</ul>
</body>
</html>
It would be simple to create the correct output through a hack, or just erasing code, but this needs to work in all situations. Such as, having no matches at all and displaying only the 'Not Specified' header and its string each time it occurs, or a mixture of both situations. The code currently works in a situation where there are no matches, and displays the 'Not Specified' header and its string each time it occurs.
It seems like if this could be done, "if it's not a match AND hasn't already been listed" it would solve the problem.
Hope that helps. If you would like more information or more files let me know. Any tips at all would be great! Thanks.
I think the main problem you are having is within your named template "definitionValueSet", where you loop over valueSet elements with an xsl:for-each
<xsl:for-each select="//v3:valueSet">
Within this you test whether the relevant attributes match your current parameter, and if you find a match, you output your list. But all valueSet elements will be searched, so even if one matches, you are still going to be processing the ones that don't and so your xsl:if for a non-match is called too. This results in the "Not Specified" being output.
What you really need to be doing is matching only the valueSet element if it exists, and if none exist that match, output your "Not Specified".
One way to do this is to first create a variable that holds the unique id of the valueSet element that matches
<xsl:variable name="valueSet">
<xsl:value-of select="generate-id(//v3:valueSet[v3:id/#root = $cur_valueSetID and v3:text/v3:reference[starts-with(#value, 'https://') and contains(#value, $cur_valueSetID)] and v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/#id = $cur_valueSetID])"/>
</xsl:variable>
Then, you can test whether this has been set, and if so, apply the template for that element. If not set, you can have your not-specified code
<xsl:choose>
<xsl:when test="$valueSet">
<xsl:apply-templates select="//v3:valueSet[generate-id() = $valueSet]"/>
</xsl:when>
<xsl:otherwise>
<!-- Not Specified -->
</xsl:otherwise>
</xsl:choose>
Here is the full XSLT, which should hopefully give the output you require
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xalan" xmlns:v3="urn:hl7-org:v3" xmlns:rvs="urn:ihe:iti:svs:2008">
<xsl:output method="html" standalone="yes" omit-xml-declaration="no" indent="yes" xalan:indent-amount="2"/>
<xsl:template match="/v3:QualityMeasureDocument">
<html>
<head>
<title>Test 'I'</title>
</head>
<body>
<ul>
<xsl:apply-templates select="//v3:dataCriteriaSection"/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="v3:dataCriteriaSection">
<xsl:for-each select=".//v3:entry">
<xsl:if test="*/v3:value/#valueSet">
<xsl:call-template name="definitionValueSet">
<xsl:with-param name="cur_valueSetID" select="*/v3:value/#valueSet"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="*/v3:code/#valueSet">
<xsl:call-template name="definitionValueSet">
<xsl:with-param name="cur_valueSetID" select="*/v3:code/#valueSet"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="*/v3:participant/v3:roleParticipant/v3:code/#valueSet">
<xsl:call-template name="definitionValueSet">
<xsl:with-param name="cur_valueSetID" select="*/v3:participant/v3:roleParticipant/v3:code/#valueSet"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="definitionValueSet">
<xsl:param name="cur_valueSetID"/>
<xsl:variable name="valueSet">
<xsl:value-of select="generate-id(//v3:valueSet[v3:id/#root = $cur_valueSetID and v3:text/v3:reference[starts-with(#value, 'https://') and contains(#value, $cur_valueSetID)] and v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/#id = $cur_valueSetID])"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$valueSet != ''">
<xsl:apply-templates select="//v3:valueSet[generate-id() = $valueSet]"/>
</xsl:when>
<xsl:otherwise>
<li>
<xsl:text>Not Specified</xsl:text>
<ul>
<li>
<xsl:text>ValueSet: </xsl:text>
<xsl:value-of select="$cur_valueSetID"/>
</li>
</ul>
</li>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="v3:valueSet">
<li>
<xsl:text>Id: </xsl:text>
<xsl:value-of select="v3:id/#root"/>
<xsl:for-each select="v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/rvs:ConceptList/rvs:Concept">
<ul>
<li>
<xsl:if test="not(#code = '')">
<xsl:if test="#code">
<xsl:text>code = </xsl:text>
<xsl:value-of select="#code"/>
<xsl:text/>
</xsl:if>
</xsl:if>
</li>
</ul>
</xsl:for-each>
</li>
</xsl:template>
</xsl:stylesheet>
Do note, as mentioned in a comment, be careful with the use of expression //v3:entry. This is an absolute expression, and will match any element at all in the document (i.e it searches from the root element downwards). If you wanted only to look under the current element, use the relative expression .//v3:entry
EDIT: For a slight variant on this, as a potentially more efficient way to look up the valueSet elements you could define a key at the top of the XSLT document
<xsl:key name="valueSet" match="v3:valueSet" use="generate-id()" />
Then, instead of doing this to look up the correct valueSet element
<xsl:apply-templates select="//v3:valueSet[generate-id() = $valueSet]"/>
You could use the key instead
<xsl:apply-templates select="key('valueSet', $valueSet)"/>
The following is a simple-ish alternate solution (does not use generate-id() or key() ) provided for comprehension. It is likely less efficient than and should not replace Tim C's excellent answer. I am simply providing this so people can learn, and to show that I put full effort into solving this issue, instead of just getting what I needed and moving on.
The solution is a fix to the original 'Problem Translator'. The only section that needs to be edited (although heavily) from that XSL file is the "definitionValueSet" named template.
First we need to create a variable $valueSetData which stores all of the values of v3:id/#root in a one time pass, with a ',' in between them, in order to be individually referenced later. It's like a new temporary database.
The for-each contains a predicate which limits the matches as per requirements. These are included within the declaration because they are local nodes there. Also, this keeps from processing extra conditional checks later.
<xsl:variable name="valueSetData">
<xsl:for-each select="//v3:valueSet[
v3:text/v3:reference[starts-with(#value, 'https://') and contains(#value, $cur_valueSetID)] and
v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/#id = $cur_valueSetID]">
<xsl:value-of select="v3:id/#root"/>
<xsl:text>,</xsl:text>
</xsl:for-each>
</xsl:variable>
Next we have the choose statement, edited for specifics. The statement determines if there is a match to the $cur_valueSetID within the $valueSetData 'database'.
The for-each predicate limits duplicate matches (with the wrong values in addition due to context).
<xsl:choose>
<xsl:when test="contains($valueSetData, $cur_valueSetID)">
<xsl:for-each select="//v3:valueSet[$cur_valueSetID = v3:id/#root]">
<!-- Display found matches which meet all requirements -->
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<!-- Display Not Specified -->
</xsl:otherwise>
</xsl:choose>
The full "definitionValueSet" template:
<xsl:template
name="definitionValueSet">
<xsl:param name="cur_valueSetID"/>
<xsl:variable name="valueSetData">
<xsl:for-each select="//v3:valueSet[
v3:text/v3:reference[starts-with(#value, 'https://') and contains(#value, $cur_valueSetID)] and
v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/#id = $cur_valueSetID]">
<xsl:value-of select="v3:id/#root"/>
<xsl:text>,</xsl:text>
</xsl:for-each>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($valueSetData, $cur_valueSetID)">
<xsl:for-each select="//v3:valueSet[$cur_valueSetID = v3:id/#root]">
<li>
<xsl:text>Id: </xsl:text>
<xsl:value-of select="v3:id/#root"/>
<xsl:for-each select="v3:text/rvs:RetrieveValueSetResponse/rvs:ValueSet/rvs:ConceptList/rvs:Concept">
<ul>
<li>
<xsl:if test="not(#code = '')">
<xsl:if test="#code">
<xsl:text>code = </xsl:text>
<xsl:value-of select="#code"></xsl:value-of>
<xsl:text/>
</xsl:if>
</xsl:if>
</li>
</ul>
</xsl:for-each>
</li>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<li>
<xsl:text>Not Specified</xsl:text>
<ul>
<li>
<xsl:text>ValueSet: </xsl:text>
<xsl:value-of select="$cur_valueSetID"></xsl:value-of>
</li>
</ul>
</li>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSL Process attributes in an order

I need to process attributes (of any node that has them) in a particular order. For example:
<test>
<event
era ="modern"
year ="1996"
quarter = "first"
day = "13"
month= "January"
bcad ="ad"
hour ="18"
minute = "23"
>The big game began.</event>
<happening
era ="modern"
day = "18"
bcad ="ad"
month= "February"
hour ="19"
minute = "24"
>The big game ended.</happening>
<other>Before time existed.</other>
</test>
This
<xsl:template match="test//*">
<div>
<xsl:apply-templates select="#*" />
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template match="#*">
<span class="{name()}">
<xsl:value-of select="."/>
</span>
</xsl:template>
would format things as I need them. That is, I'd get
<div><span class="era">modern</span>
<span class="year">1996</span>
<span class="quarter">first</span>
<span class="day">13</span>
<span class="month">January</span>
<span class="bcad">ad</span>
<span class="hour">18</span>
<span class="minute">23</span>The big game began.</div>
<div><span class="era">modern</span>
<span class="day">18</span>
<span class="bcad">ad</span>
<span class="month">February</span>
<span class="hour">19</span>
<span class="minute">24</span>The big game ended.</div>
<div>Before time existed.</div>
(though without the newlines I've added here for legibility).
But the order of the attributes wouldn't (necessarily) be right.
To fix that, I could change <xsl:apply-templates select="#*" /> to <xsl:call-template name="atts" /> and add a template that applies templates in the needed order, like this:
<xsl:template match="test//*">
<div>
<xsl:call-template name="atts" />
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template name="atts">
<xsl:apply-templates select="#era" />
<xsl:apply-templates select="#bcad" />
<xsl:apply-templates select="#year" />
<xsl:apply-templates select="#quarter" />
<xsl:apply-templates select="#month" />
<xsl:apply-templates select="#day" />
<xsl:apply-templates select="#hour" />
<xsl:apply-templates select="#minute" />
</xsl:template>
<xsl:template match="#*">
<span class="{name()}">
<xsl:value-of select="."/>
</span>
</xsl:template>
Is this the best-practices way to process attributes in a specified order? I keep wondering if there is a way that uses keys or a global variable.
I need to use XSLT 1.0 and in the real case, there are several dozen attributes, not just eight.
In contrast to elements for example, the order of attributes is not significant in XML, i.e. XPath and XSLT may process them in any order. Thus, the only way to force a given order, is to specify it somehow. One way is to call them explicitly as in your last code example. You can also extract all attribute names and store them in a separate XML file, e.g. something like
<attributes>
<attribute>era</attribute>
<attribute>year</attribute>
<attribute>month</attribute>
...
<attributes>
Now you can load these elements with the document() function and iterate over all attribute elements:
<xsl:variable name="attributes" select="document('attributes.xml')//attribute"/>
...
<xsl:template match="*">
<xsl:variable name="self" select="."/>
<xsl:for-each select="$attributes">
<xsl:apply-templates select="$self/#*[name()=current()]"/>
</xsl:for-each>
</xsl:template>

How to remove some tags without removing the content in xslt and pass entire content XML as input to another template

I am working on XSLT.
Source XML:
<?xml version="1.0" encoding="iso-8859-1"?>
<Content>
<alertHeader>
<ol xmlns="http://www.w3.org/1999/xhtml">
<li>
<strong>Review</strong>
your current available balance. It can be obtained 24 hours a day, 7 days a week through
Account Activity
any 123 ATM or by calling
<a id="dynamicvariable" href="#" name="Customercare">[Customercare]</a>
at
<a id="dynamicvariable" href="#" name="contactNo">[contactNo]</a>
.
</li>
<li>
<strong>Take into consideration</strong>
<ul>
<li>Please get in touch with us</li>
<li>Please consider this as important info</li>
</ul>
</li>
<li>
<strong>Current</strong>
Statementt doesnot match the requirement
<a id="dynamicvariable" href="#" name="Actual acccount">[Actual acccount]</a>
,plus
<a id="dynamicvariable" href="#" name="totalcharges">[totalcharges]</a>
Make u r response as positive.
</li>
</ol>
<p xmlns="http://www.w3.org/1999/xhtml"></p>
<div xmlns="http://www.w3.org/1999/xshtml"></div>
<span xmlns="http://www.w3.org/1999/xshtml"></span>
</alertHeader>
</Content>
I want to write a XSLT to pass entire content in the tag alertHeader as value to another template.
I want to modify this code as follows.
1.Remove the tags <p></p>, and <div></div>,<span></span> and <a></a>. I want to remove only tags but not the value of the tags. It should be there as it is.
2.Pass the content including tags to "Process" template.
Output required:
<aaa>
<text>
<ol xmlns="http://www.w3.org/1999/xhtml">
<li>
<strong>Review</strong>
your current available balance. It can be obtained 24 hours a day, 7 days a week through
<dynamicvariable name="Customercare"/>
at
<dynamicvariable name="contactNo"/>
.
</li>
<li>
<strong>Take into consideration</strong>
<ul>
<li>Please get in touch with us</li>
<li>Please consider this as important info</li>
</ul>
</li>
<li>
<strong>Current</strong>
Statementt doesnot match the requirement
</li>
</ol>
</text>
</aaa>
current XSLT:
<?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"/>
<xsl:template match="alertHeader">
<xsl:call-template name="process">
<xsl:with-param name="text" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="process">
<xsl:param name="text" />
<xsl:variable name="head" select="substring-before($text, '[')" />
<xsl:variable name="tag" select="substring-before(substring-after($text, '['), ']')" />
<xsl:variable name="tail" select="substring-after($text, ']')" />
<xsl:choose>
<xsl:when test="$head != '' and $tag != ''">
<xsl:value-of select="$head" />
<dynamicVariable name="{$tag}" />
<!-- recursive step: process the remainder of the string -->
<xsl:call-template name="process">
<xsl:with-param name="text" select="$tail" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Can any one say what all the changes required for my code.
Thanks.
There is no need to do any initial processing of the XML document and then pass it to the process template. From looking at your question, it looks like you have a requirement (which you haven't explicitly mentioned) to transform 'tags' in the text, such as form '[Total_Fee]', to dynamicVariable elements.
So, what you need to do is firstly just have a template to ignore your chosen nodes, but continue matching the elements and text within them.
<xsl:template match="Content|alertHeader|xhtml:p|xshtml:div|xshtml:span|xhtml:a">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
Do note the complication here is that you have defined different namespaces for some of your nodes (and these would have to be declared in the XSLT document). If you have multiple namespaces, you could also do the following.
<xsl:template match="Content|alertHeader|*[local-name() = 'p']|*[local-name() = 'span']|*[local-name() = 'div']|*[local-name() = 'a']">
Without namespaces you could do the following
<xsl:template match="Content|alertHeader|p|div|span|a">
Next, your named process template can then be combined to match text() elements
<xsl:template match="text()" name="process">
This allows it to match text elements, and recursively call itself to look for tags within the text.
Here is the full XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:xshtml="http://www.w3.org/1999/xshtml"
exclude-result-prefixes="xhtml xshtml">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Content|alertHeader|xhtml:p|xshtml:div|xshtml:span|xhtml:a">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="text()" name="process">
<xsl:param name="text" select="." />
<xsl:choose>
<xsl:when test="contains($text, ']') and contains($text, '[')">
<xsl:value-of select="substring-before($text, '[')"/>
<dynamicVariable name="{substring-before(substring-after($text, '['), ']')}"/>
<xsl:call-template name="process">
<xsl:with-param name="text" select="substring-after($text, ']')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*|*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your XML, the following is output
<ol xmlns="http://www.w3.org/1999/xhtml">
<li>
<strong>Review</strong> your current available balance. It can be obtained 24 hours a day, 7 days a week through Account Activity any Wells Fargo ATM or by calling <dynamicVariable name="Call_Center_Name" xmlns="" /> at <dynamicVariable name="Phone_Number" xmlns="" /> . </li>
<li>
<strong>Take into account</strong>
<ul>
<li>Your pending transactions and any additional transactions that have not yet been deducted from your available balance, such as checks you have written or upcoming scheduled automatic payments.</li>
<li>Any transactions that have been returned because you did not have enough money in your account at that time; they may be resubmitted for payment by the person or party who received the payment from you</li>
</ul>
</li>
<li>
<strong>Deposit</strong> enough money to establish and maintain a positive account balance. A deposit of at least <dynamicVariable name="Absolute_Available_Balance" xmlns="" /> ,plus <dynamicVariable name="Total_Fee" xmlns="" /> in fees, would have been required to make your account balance positive at the time we sent this notice. </li>
</ol>
The problem in xslt-1.0 is that you can't access a result node or nodeset, as in there is no XPath or function or anything to refer to the transformation result of a template.
Is it essential that you call process as it is? The way I'd go about this is to make process a template that transforms exactly one node, and then call it from every node below alertHeader, like:
<xsl:template match="alertHeader">
<!-- switch modes so we know we need to call process -->
<xsl:apply-templates mode="callproc"/>
</xsl:template>
<xsl:template match="*" mode="callproc">
<!-- call process on THIS node -->
<xsl:call-template name="process">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
<!-- descend -->
<xsl:apply-templates mode="callproc"/>
</xsl:template>
<!-- all the stuff that needs stripping -->
<xsl:template match="p|div|span|a" mode="callproc">
<!-- call process on the text() portion -->
<xsl:call-template name="process">
<xsl:with-param name="text" select="text()"/>
</xsl:call-template>
<!-- no descent here -->
</xsl:template>
Update:
Without process template
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xh="http://www.w3.org/1999/xhtml"
xmlns:xsh="http://www.w3.org/1999/xshtml">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Content">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="alertHeader">
<xsl:element name="aaa">
<xsl:element name="text">
<!-- switch modes so we know we need to call process -->
<xsl:apply-templates/>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<!-- descend -->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- all the stuff that needs stripping -->
<xsl:template match="xh:p|xh:div|xh:span|xsh:div|xsh:span">
<xsl:apply-templates/>
</xsl:template>
<!-- was process -->
<xsl:template match="xh:a[#name]">
<xsl:element name="dynamicvariable" namespace="http://www.w3.org/1999/xhtml">
<xsl:attribute name="name">
<xsl:value-of select="#name"/>
</xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="xh:a"/>
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Output:
<ol xmlns="http://www.w3.org/1999/xhtml">
<li>
<strong>Review</strong>
your current available balance. It can be obtained 24 hours a day, 7 days a week through
any Wells Fargo ATM or by calling
<dynamicvariable name="Call_Center_Name"/>
at
<dynamicvariable name="Phone_Number"/>
.
</li>
<li>
<strong>Take into account</strong>
<ul>
<li>Your pending transactions and any additional transactions that have not yet been deducted from your available balance, such as checks you have written or upcoming scheduled automatic payments.</li>
<li>Any transactions that have been returned because you did not have enough money in your account at that time; they may be resubmitted for payment by the person or party who received the payment from you</li>
</ul>
</li>
<li>
<strong>Deposit</strong>
enough money to establish and maintain a positive account balance. A deposit of at least
<dynamicvariable name="Absolute_Available_Balance"/>
,plus
<dynamicvariable name="Total_Fee"/>
in fees, would have been required to make your account balance positive at the time we sent this notice.
</li>
</ol>
</text>
</aaa>

XSL counter on specific condition for SharePoint 2010 CQWP

This is the first time I have ever posted a question so apologese in advance if I jibber here.
I am trying to put together a CQWP with jQuery tabs slider functionality. The HTML I want to output should be in the form of 2 UL's. The first with li anchor tags with #associated-ul-id
The second ul's should have ids that associate with the list items in the first. Eg
<div id="tabs" class="news">
<div class="news-pagination">
« Prev
<ul id="carouseltext" class="horizontal-text order">
<li>System</li>
<li>School</li>
</ul>
» Next
<div class="clear"> </div>
</div>
<ul id="tabs-1" class="feed order">
<li>title 1</li>
<li>title 2</li>
</ul>
<ul id="tabs-2" class="feed order">
<li>title 3</li>
</ul>
</div>
The original XML starts off in the form
My XSL goes through the XML twice to fill the 2 ul's. The first time it just adds a new list item when the __begincolumn and __begingroup variables are true. I striped down the functionality here to just output the header. Here's a stripped down version of the XSL
<xsl:template match="/">
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row" />
<xsl:variable name="RowCount" select="count($Rows)" />
<xsl:variable name="FirstRow" select="1" />
<xsl:param name="ColNumber" select="1" />
<xsl:for-each select="$Rows" >
<xsl:variable name="CurPosition" select="position()" />
<xsl:variable name="BeginNewsItemsList1" select="string('<ul id="tabs-')" />
<xsl:variable name="BeginNewsItemsList2" select="string('"class="feed order">')" />
<xsl:variable name="BeginNewsItemsList" select="concat($BeginNewsItemsList1, $ColNumber, $BeginNewsItemsList2)" />
<xsl:if test="($CurPosition >= $FirstRow and $CurPosition <= $LastRow)">
<xsl:variable name="StartNewGroup" select="#__begingroup = 'True'" />
<xsl:variable name="StartNewColumn" select="#__begincolumn = 'True'" />
<xsl:when test="$StartNewGroup and $StartNewColumn">
<xsl:choose>
<xsl:when test="$CurPosition = $FirstRow">
<xsl:value-of disable-output-escaping="yes" select="$BeginNewsItemsList" />
</xsl:when>
<xsl:otherwise>
<!-- other instructions -->
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$StartNewGroup">
<xsl:call-template name="OuterTemplate.CallFooterTemplate"/>
<xsl:value-of disable-output-escaping="yes" select="concat($EndColumn, $BeginNewsItemsList)" />
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="OuterTemplate.Count">
<xsl:param name="ColNumber" />
<xsl:value-of select="$ColNumber + 1" />
</xsl:template>
For the second for-each loop I'm having trouble setting up a counter so that I can add the number to the end of the id for each new list id="tabs-1", id="tabs-2", etc.
In theory I think I should set a parameter outside my for-each loop and then in the loop call a template that gets the parameter value and increments it. That would mean it would increment only when the template is called.
I can't use position() for this as it doesn't correspond to the values I want. I've tried to follow a couple a few blogs about recursive programming with xsl, but I can't seem to find anything that works. I'm sure I'm just writing the XSL wrong, but I'm having a bit of a brain dump now.
If anybody could point me in the right direction that would be awesome. Thanks very much.
You can't change variable's values after declaration. You can use them in expressions and/or pass as parameters. Thus, you can't use outside variable as counter explicitly. One available trick is recursive cycle like:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="root">
<HTML>
<BODY>
<xsl:call-template name="for">
<xsl:with-param name="i" select="1"/>
<xsl:with-param name="n" select="5"/>
</xsl:call-template>
</BODY>
</HTML>
</xsl:template>
<xsl:template name="for">
<xsl:param name="i"/>
<xsl:param name="n"/>
<xsl:value-of select="$i"/>
<xsl:if test="$i < $n">
<xsl:text>, </xsl:text>
<xsl:call-template name="for">
<xsl:with-param name="i" select="$i+1"/>
<xsl:with-param name="n" select="$n"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
result:
1, 2, 3, 4, 5