Trying to obtain first item after XSLT sort in Sitecore - xslt

I have XML that looks like this
<xml>
<news>
<newsitem>
<publishdate>2011-10-11</publishdate>
<title>Article 1</title>
<breakingnewsflag>false</breakingnewsflag>
</newsitem>
<newsitem>
<publishdate>2009-10-14</publishdate>
<title>Article 2</title>
<breakingnewsflag>true</breakingnewsflag>
</newsitem>
<newsitem>
<publishdate>2009-10-12</publishdate>
<title>Article 3</title>
<breakingnewsflag>true</breakingnewsflag>
</newsitem>
</news>
</xml>
Now what I want to do is obtain the most recent item by date, that has the breakingnewsflag set to true.
It seems I can filter on the flag when doing a for-each
<xsl:for-each select="sc:item('/sitecore/content/Home',.)/item[sc:fld('BreakingNewsStory',.)]">
and I can sort inside this collection
<xsl:sort select="sc:fld('PublishDate',.)" order="ascending" />
which leaves me with XSLT that looks like this...
<xsl:for-each select="sc:item('/sitecore/content/Home',.)/item[sc:fld('BreakingNewsStory',.)]">
<xsl:sort select="sc:fld('PublishDate',.)" order="ascending" />
Article Name: <xsl:value-of select="#name" /><br />
Title: <sc:text field="title"/> <br />
</xsl:for-each>
...but I cannot then obtain the first item.
I can obtain the first item after checking the flag by using this syntax
<xsl:for-each select="sc:item('/sitecore/content/Home',.)/item[sc:fld('BreakingNewsStory',.)][1]">
...but that will then grab it before the ordering.
I was about to try putting another foreach after the sort clause, to then obtain the first item, but I figured that was going to be needlessly complicated, and there had to be an easier way to do what I'm trying to do.

If I'm understanding this correctly, you could try using position() to get the first item :
<xsl:for-each select="sc:item('/sitecore/content/Home',.)/item[sc:fld('BreakingNewsStory',.)]">
<xsl:sort select="sc:fld('PublishDate',.)" order="ascending" />
<xsl:if test="position() = 1">
Article Name: <xsl:value-of select="#name" /><br />
Title: <sc:text field="title"/> <br />
</xsl:if>
</xsl:for-each>

Related

How to conditionally filter out children in XSLT

I currently have something similar to the following XML:
<div class="newsFeed">
<div class="newsItem"><news position="3"/></div>
<categoryFilter dayFilter="4">
<div class="newsItem"><news position="2"/></div>
</categoryFilter>
</div>
I need to copy the XML, and output the nth news item on the node. Futhermore, I need to be able to filter that news. For this example, lets construct my news as follows:
<xsl:variable name="news">
<xsl:for-each select="1 to 30">
<item>
<day><xsl:value-of select=". mod 4" /></day>
<content>Content: <xsl:value-of select="." /></content>
</item>
</xsl:for-each>
</xsl:variable>
I actually use document() and use the for-each to sort it, but I'm trying to keep it succinct. This would mean that my output XML would be something like the following:
<div class="newsFeed">
<div class="newsItem">Content: 3</div>
<div class="newsItem">Content: 8</div>
</div>
The reason the second one is 8 is because the categoryFilter filters out every <item> where the day isn't 4 (which happens to be the 4th, 8th, 12th, and so on), and then we select the second one.
The XSLT to produce the above is as follows:
XSLT:
<xsl:template match="news">
<xsl:param name="items" select="$news" />
<xsl:variable name="position" select="#position" />
<xsl:copy-of select="$items/item[position()=$position]/content" />
</xsl:template>
<xsl:template match="categoryFilter">
<xsl:param name="items" select="$news" />
<xsl:variable name="day" select="#dayFilter" />
<xsl:variable name="filteredItems">
<xsl:for-each select="$items/item[day=$day]">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:variable>
<xsl:apply-templates>
<xsl:with-param name="items" select="$filteredItems">
</xsl:apply-templates>
</xsl:template>
My problem lies with the <for-each>. It seems silly that I have to use a for-each to filter out the <item> nodes, but I can't find a better way. Simply doing a <xsl:variable select="$items/item[day=$day]"> changes the structure, and makes it so that the <xsl:template match="news"> doesn't work.
Is there a way to filter out child nodes without using a for-each? I am using <xsl:stylesheet version="3.0">
Instead of doing this...
<xsl:variable name="filteredItems">
<xsl:for-each select="$items/item[day=$day]">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:variable>
... you could use a sequence. This will select the actual items, as opposed to create copies of them
<xsl:variable name="filteredItems">
<xsl:sequence select="$items/item[day=$day]" />
</xsl:variable>
This was, doing $filteredItems/item will still work.
Alternatively, you could take the opposite approach, and do away with the need to specify /item in all the expressions.
First, define your news variable like so:
<xsl:variable name="news" as="element()*">
This means you can write the expression that uses it like so:
<xsl:copy-of select="$news[position()=$position]/content" />
And similarly for filteredItems....
<xsl:variable name="filteredItems" select="$items[day=$day]" />

BizTalk XSLT Transformation Script Nested Loops

I am pretty much new to BizTalk, have been working on a solution and wasnot able to get the transformation done with built-in functoids (Loop, table loops etc. etc.)
Seems like it can be easily done with Inline XSLT script, I tried that getting orchestration error probably something wrong with the script.
Can someone assist me with that?
Thanks Much
Ramsey
This is the map:
I want to transfor data If Part_0 PolicyNumber = Part_1 PolicyNumber
http://psrathoud.files.wordpress.com/2014/09/2014-09-09-08_43_37-map.jpg
and below is the script
<xsl:template name="ValidateTransactionData">
<!-- Loop MessagePart_0 -->
<xsl:for-each select="InputMessagePart_0/s1:MergedINTWAW_Root/Merged_DATA">
<!-- Create "Matching_DATA" record in the output message for each matching transaction-->
<Matched_DATA>
<xsl:variable name="s0_Source" select="Source"/>
<xsl:variable name="s0_Insured" select="Insured"/>
<xsl:variable name="s0_PremiumAmount" select="PremiumAmount"/>
<xsl:variable name="s0_LongPolicy" select="LongPolicyNumber"/>
<!-- Copy transaction details from InputMessagePart_0 to the output message-->
<Source>
<xsl:value-of select="Source" />
</Source>
<Insured>
<xsl:value-of select="Insured" />
</Insured>
<PremiumAmount>
<xsl:value-of select="PremiumAmount" />
</PremiumAmount>
<LongPolicyNumber>
<xsl:value-of select="LongPolicyNumber" />
</LongPolicyNumber >
<!-- Loop InputMessagePart_1 and validate matching long policy number -->
<xsl:for-each select="../../../InputMessagePart_1/s1:TAM_Root/TAM_DATA">
<xsl:if test="string(LongPolicyNumber)=$s0_LongPolicyNumber">
<Source><xsl:value-of select="Source" /></Source>
<Insured><xsl:value-of select="Insured" /></Insured>
<PremiumAmount><xsl:value-of select="PremiumAmount" /></PremiumAmount>
<LongPolicyNumber><xsl:value-of select="LongPolicyNumber" /></LongPolicyNumber >
</xsl:if>
</xsl:for-each >
<xsl:value-of select="./text()" />
</Matched_DATA >
<xsl:value-of select="./text()" />
</xsl:for-each>
<xsl:value-of select="./text()" />
</xsl:template >

Using XSL Choose to Substitute Empty String

I'm trying to substitute an empty value in a csv file with a number.
Here's an example:
1111111,,11222
So I tried this:
<xsl:template match="/">
<xsl:apply-templates select="//tr" />
</xsl:template>
<xsl:template match="tr">
<document>
<content name="title">
<xsl:value-of select="td[1]/text()" />
</content>
<content name="loanID">
<xsl:value-of select="td[1]/text()" />
</content>
<content name="cNumber">
<xsl:variable name="score" select="td[2]/text()" />
<xsl:choose>
<xsl:when test="$score=''">
<xsl:value-of select="550" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="td[18]/text()" />
</xsl:otherwise>
</xsl:choose>
</content>
</document>
</xsl:template>
I constantly get a null value for the cNumber node when the value is empty in the row, and I'm expecting my code to substitute the empty value for '550'. What am I doing wrong? I checked this question here: and it seems like this should work. I'm using a special application for this but my guess is the fault lies with me.
Thanks
If the td element is empty, then td/text() returns an empty sequence/node-set, and when you compare an empty sequence to '', the result is false. This is one of the reasons that many people advise against using text(). You're not interested here in the text node, you are interested in the string value of the td element, and to get that you should use
<xsl:variable name="score" select="string(td[2])" />
Your other uses of text() are also incorrect, though you're only likely to see a problem if your XML input contains comments or processing instructions. But you should get out of this coding habit, and replace
<xsl:value-of select="td[1]/text()" />
by
<xsl:value-of select="td[1]" />
As a general rule, when you see /text() in an XPath expression it's usually wrong.

Displaying xml nodes dynamically and applying style to specific nodes using recursion

Below is my xml file.
<xml>
<top>
<main>
<firstname>John</firstname>
<lastname>John</lastname>
<table></table>
<chapter>
<firstname>Alex</firstname>
<lastname>Robert</lastname>
<p>Sample text chap</p>
<figure name="f1.svg"></figure>
<chapter>
<firstname>Rebec</firstname>
<lastname></lastname>
<p>Sample text</p>
<figure name="f2.svg"></figure>
</chapter>
</chapter>
</main>
</top>
</xml>
Desired output:
<bold>John
table
<bold>Robert
Sample text chap
f1.svg
<bold> Rebec
Sample text
f2.svg
Explaination: I have written an xslt to do this. I need to fetch the xml nodes dynamically. I cannot write: xsl:apply-templates select='main/lastname'. Because my xml format could change anytime.
I have tried a logic to first fetch all the xml nodes using '$root/*'. Then if 'table' element is encountered, i use xsl:apply-templates select='current()[name() = 'TABLE']' and perform table creation operations.
This works fine. I get the desired output but my figure elements only displays f1.svg at every place in the output. f2.svg is not shown.
And how do I match only 'lastname' and make it bold?
I want to make the code as generic/modular as possible so that it loops through all the elements of the xml tree and does some formatting on the specific nodes.
Below is a recursive xslt. With this my data is getting repeated. I am writing recursive template because xslt is not sequential.
XSLT:
<xsl:call-template name="FetchNodes">
<xsl:with-param name="endIndex" select="$NumberOfNodes" />
<xsl:with-param name="startIndex" select="1" />
<xsl:with-param name="context" select="$root/*" />
</xsl:call-template>
<xsl:template name="FetchNodes">
<xsl:param name="endIndex" />
<xsl:param name="startIndex" />
<xsl:param name="context" />
<xsl:if test="$startIndex <= $endIndex">
<xsl:if test="$context[$startIndex][name() = 'table']"">
<xsl:apply-templates select="$context[$startIndex][name() = 'table']"" mode="table" />
</xsl:if>
<xsl:call-template name="FetchNodes">
<xsl:with-param name="endIndex" select="$endIndex" />
<xsl:with-param name="startIndex" select="$startIndex + 1"/>
<xsl:with-param name="context" select="$context" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="node()" mode="table">
<xsl:value-of select="node()" />
</xsl:template>
With the above xslt, something is incorrect in the xpath of apply templates. Output is not proper.
I want XSL FO output.
Can anybody suggest something?
The problem it displaying "f1.svg" instead of "f2.svg" is because of this line
<xsl:variable name="ImageName">
<xsl:value-of select="$root/*/chapter/figure/#name" />
</xsl:variable>
You are already positioned on a figure at this point, so you only need to use a relative xpath expression here. The one you are currently using is an absolute path and so will always return the first #name attribute regardless of your context. It should look this this
<xsl:variable name="ImageName">
<xsl:value-of select="#name" />
</xsl:variable>
Or better still, like this
<xsl:variable name="ImageName" select="#name" />
Having said, the code is in a template that is trying to match an element a FIGURE element, which does not exist in the XML you have shown us. You can actually simplify the template match to this, for example
<xsl:template match="figure" mode="figure">
As for making things bold, you can just add the font-weight attribute to any block you want to make bold. Something like this:
<xsl:choose>
<xsl:when test="self::lastname">
<fo:inline font-weight="bold"><xsl:value-of select="text()" /></fo:inline>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="text()" />
</xsl:otherwise>
</xsl:choose>
EDIT: Having said all that, you may not be taking the correct approach to the problem. It may be better to use template matching, taking advantage of XSLT's built-in template to navigate over the document. Essentially, just write a template for each element you want to match, and generate the output, and then carry on matching its children.
For example, to turn a chapter into an fo:block do this
<xsl:template match="chapter">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
To output the firstname in bold, do this
<xsl:template match="firstname">
<fo:inline font-weight="bold">
<xsl:value-of select="text()"/>
</fo:inline>
</xsl:template>
To turn a figure into an image, do this (Note the use of Attribute Value Templates here, the curly braces indicate an expression to be evaluated, not output literally)
<xsl:template match="figure">
<fo:block>
<fo:external-graphic src="../resources/{#name}" content-height="60%" scaling="uniform" padding-left="2cm"/>
</fo:block>
</xsl:template>
Try this XSLT as a starting point, and build on it
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="main">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="chapter">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="firstname">
<fo:inline font-weight="bold">
<xsl:value-of select="text()"/>
</fo:inline>
</xsl:template>
<xsl:template match="lastname"/>
<xsl:template match="figure">
<fo:block>
<fo:external-graphic src="../resources/{#name}" content-height="60%" scaling="uniform" padding-left="2cm"/>
</fo:block>
</xsl:template>
</xsl:stylesheet>

How do I get an XML node with xpath in a loop, based on an attribute with the same name as the attribute in the tree I'm searching?

I cant really formulate that better, so I'll go with an example instead:
XML:
<root>
<foo>
<bar id="1">sdf</bar>
<bar id="2">sdooo</bar
</foo>
<feng>
<heppa id="4">hihi</heppa>
<heppa id="2">sseeapeea</heppa>
<heppa id="1">....</heppa>
</feng>
</root>
XSLT:
<xsl:for-each select="/root/foo/bar">
<p>
<xsl:value-of select="." />: <xsl:value-of select="/root/feng/heppa[#id = #id]" />
</p>
</xsl:for-each>
Desired output:
<p>sdf: ....</p>
<p>sdooo: sseeapeea</p>
Actual output:
<p>sdf: hihi</p>
<p>sdooo: hihi</p>
For selecting nodes with XPath 1.0 only, you need to do a node set comparison:
/root/feng/heppa[#id=/root/foo/bar/#id]
Of course, this has NxM complexity (as the others XSLT solutions)
With XSLT 1.0 you should use keys because there are cross references:
<xsl:key name="kBarById" select="bar" use="#id"/>
<xsl:template match="/root/feng/heppa[key('kBarById',#id)]">
<p>
<xsl:value-of select="concat(key('kBarById',#id),': ',.)"/>
</p>
</xsl:template>
I assume you mean /root/foo/bar since /root/foo elements don't have id.
You're comparing the #id with itself, so of course it's true for the first node examined. You can use current() to reference the current node in an expression:
<xsl:for-each select="/root/foo/bar">
<p>
<xsl:value-of select="." />: <xsl:value-of select="/root/feng/heppa[#id = current()/#id]" />
</p>
</xsl:for-each>
Another solution is to read the id attribute into a variable.
<xsl:for-each select="/root/foo/bar">
<xsl:variable name="id" select="#id"/>
<p>
<xsl:value-of select="." />: <xsl:value-of select="/root/feng/heppa[#id = $id]" />
</p>
</xsl:for-each>
This might be handier, if your real use case is more complicated and you need to use the value of the id multiple times in this for-each section.