Need Counter Value in Muenchian grouping logic code - xslt

I have requirement to print the incremental value within a for-each (Muenchian grouping) function
which I have already used.
Please find the snippet code
<xsl:template>
<xsl:for-each select="./DocumentPayable/DocumentPayableLine[generate-id() = generate-id(key('Locationabc', ./Tax/TaxRateCode))]">
<xsl:if test="(LineType/Code='AWT')">
<xsl:variable name="LocationCode" select="./Tax/TaxRateCode"/>
<Rcrd>
<AddtlInf>/D1/<xsl:value-of select="(Tax/TaxRateCode)"/></AddtlInf>
</Rcrd>
</xsl:if>
</xsl:for-each>
</xsl:template>
Expected Output:
/D1/INDIA
/D2/USA
/D3/AFRICA
Based on number of tax rate codes the incremental value should be shown.
Please help.

Use a predicate and position() e.g.
<xsl:for-each select="./DocumentPayable/DocumentPayableLine[generate-id() = generate-id(key('Locationabc', Tax/TaxRateCode))][LineType/Code='AWT']">
<Rcrd>
<AddtlInf>/D<xsl:value-of select="position()"/>/<xsl:value-of select="Tax/TaxRateCode"/></AddtlInf>
</Rcrd>
</xsl:for-each>

Related

XSLT: Get Element Name of "Tokenized Item"

Is there a way to gather element name of a tokenized value? I have been trying to do it but it is giving me an error "[Saxon-PE 9.6.0.7] XPTY0004: Required item type of first argument of name() is node(); supplied value has item type xs:string"
Here are my sample set of data:
<SET>
<REAL_TAGNAME> 1 2 3 4 </REAL_TAGNAME>
</SET>
If I have use this code:
<xsl:for-each select="SET/REAL_TAGNAME">
<xsl:for-each select="tokenize(normalize-space(.),'\s+')">
<Hardcode_Tag>
<xsl:value-of select="."/>
</Hardcode_Tag>
</xsl:for-each>
</xsl:for-each>
then I will successfully have the following:
<Hardcode_Tag>1</Hardcode_Tag>
<Hardcode_Tag>2</Hardcode_Tag>
<Hardcode_Tag>3</Hardcode_Tag>
<Hardcode_Tag>4</Hardcode_Tag>
But I want to move away from hard-coding and would like to use its original tag name to have something like:
<REAL_TAGNAME>1</REAL_TAGNAME>
<REAL_TAGNAME>2</REAL_TAGNAME>
<REAL_TAGNAME>3</REAL_TAGNAME>
<REAL_TAGNAME>4</REAL_TAGNAME>
While I try below with the xsl:element, it keeps giving me an error mentioned above:
<xsl:for-each select="SET/REAL_TAGNAME">
<xsl:for-each select="tokenize(normalize-space(.),'\s+')">
<xsl:element name="{name(.)}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
does anyone have any idea on how I can fix this? Thanks in advance for your help!
Within the xsl:for-each, the expression . refers to the current item, i.e. the current token extracted from the string value of the element. If you want to remember the name of the element that was the current element before you entered the for-each, just set a variable before the inner xsl:for-each:
<xsl:for-each select="SET/REAL_TAGNAME">
<xsl:variable name="element-name" select="name(.)"/>
<xsl:for-each select="tokenize(normalize-space(.),'\s+')">
<xsl:element name="{$element-name}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
In the code you show, of course, the value of $element-name will invariably be 'REAL_TAGNAME'. I'm assuming that's not true in the general case.

Incrementer in Nested for-each-group Calls

First post here after looking at tons of awesome suggestions from the community.
I have three fields in XSLT 2.0, all at the same level (shoulders, knees, and toes). I am needing to output sums of toes based on unique combinations of shoulders and knees, so I have created two nested for-each-groups. On each output, I'm also needing to output an incrementer from from 1 to number of unique combinations of shoulders and knees.
This incrementer is where I'm having issues. The closest I've come is by calling position(), but if I call it in the innermost group, the counter resets at each unique shoulder. If I call it in the outermost group, every knee inside of a unique shoulder gets the same value, then it resets at each unique shoulder. If I call it outside of the groups completely, it never gets past 1. I've also tried to use xsl:number , keys, etc., to no avail. In those cases, the correct number of rows are still being printed, but the incrementer values are looking at the individual, non-grouped values.
I read one suggestion about "tunneling" values between templates, but I haven't been able to get that to work, mostly because I don't think I'm invoking the templates correctly (with these fields being same-level and not parent-child). Any thoughts on making this work with for-each-group or otherwise? Many thanks in advance.
Sample XML:
<bodies>
<parts>
<shoulders>shoulders1</shoulders>
<knees>knees1</knees>
<toes>1</toes>
</parts>
<parts>
<shoulders>shoulders2</shoulders>
<knees>knees2</knees>
<toes>2</toes>
</parts>
<parts>
<shoulders>shoulders1</shoulders>
<knees>knees2</knees>
<toes>10</toes>
</parts>
<parts>
<shoulders>shoulders2</shoulders>
<knees>knees1</knees>
<toes>10</toes>
</parts>
<parts>
<shoulders>shoulders1</shoulders>
<knees>knees1</knees>
<toes>9</toes>
</parts>
<parts>
<shoulders>shoulders2</shoulders>
<knees>knees2</knees>
<toes>8</toes>
</parts>
</bodies>
Sample XSLT:
<xsl:stylesheet exclude-result-prefixes="xsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:this="urn:this-stylesheet" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:for-each-group select="bodies/parts" group-by="shoulders">
<xsl:for-each-group select="current-group()" group-by="knees">
<xsl:value-of select="shoulders"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="knees"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="sum(current-group()/toes)"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="position()"/>
<xsl:text>. </xsl:text>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Resulting Output:
shoulders1, knees1, 10, 1. shoulders1, knees2, 10, 2. shoulders2, knees2, 10, 1. shoulders2, knees1, 10, 2.
Desired Output:
shoulders1, knees1, 10, 1. shoulders1, knees2, 10, 2. shoulders2, knees2, 10, 3. shoulders2, knees1, 10, 4.
When you've got two nested xsl:for-each-group instructions like this, then an alternative is to do single level grouping on a composite key, like this:
<xsl:for-each-group select="bodies/parts" group-by="concat(shoulders, '~', knees)">
The position() will then increment the way you are looking for, if I've understood the requirement correctly.
This doesn't work, of course, if you actually want to produce hierarchically structured output in which the outer group is significant.
I've tried this three times now and I think you have to process your results and then count them. I've run out of time to try and figure a way to do the counting inline, because I think it can't be done.
<xsl:stylesheet exclude-result-prefixes="xsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:this="urn:this-stylesheet" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:variable name="results" as="xsd:string*">
<xsl:for-each-group select="bodies/parts" group-by="shoulders">
<xsl:for-each-group select="current-group()" group-by="knees">
<xsl:value-of>
<xsl:value-of select="shoulders"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="knees"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="sum(current-group()/toes)"/>
<xsl:text>,</xsl:text>
</xsl:value-of>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:variable>
<xsl:for-each select="$results">
<xsl:value-of select=".,position()"/>
<xsl:text>. </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0: if statement when position matches a variable value

I'm trying to insert an IF statement into an existing FOR-EACH loop to do something slightly different if it matches a variable from another node (the node I want is actually a sibling of it's parent - if that makes sense!?).
The value is a simple integer. I basically want to say: If the position is equal to the variable number then do XXXX.
Here is the XSLT, it's only v1.0 and not 2.0 that I can use.
<xsl:for-each select="/Properties/Data/Datum[#ID='ID1']/DCR[#Type='accordion_tab']/accordion_tab/sections">
<h3 class="accordionButton">
<xsl:if test="position()='openpane value to go here'">
<xsl:attribute name="class">
<xsl:text>new text</xsl:text>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
My XML extract is here:
<sections>
<title>title</title>
<text>some text</text>
</sections>
<openpane>2</openpane>
You didn't make this clear in your question, but I assume you iterate over the sections elements in your for-each loop. From the for-each loop you can reach the openpane element by going through the parent of the current sections element:
<xsl:for-each select="sections">
<xsl:if test="position() = ../openpane">
...
</xsl:if>
</xsl:for-each>
You could also define a variable referring to the openpane element first:
<xsl:variable name="openpane" select="openpane"/>
<xsl:for-each select="sections">
<xsl:if test="position() = $openpane">
...
</xsl:if>
</xsl:for-each>

XSLT for-each counter - how to access data

for performance testing purposes I want to take a small XML file and create a bigger one from it - using XSLT. Here I plan to take each entity (Campaign node in the example below) in the original XML and copy it n times, just changing its ID.
The only way I can think of to realize this, is a xsl:for-each select "1 to n". But when I do this I do not seem to be able to access the entity node anymore (xsl:for-each select="campaigns/campaign" does not work in my case). I am getting a processor error: "cannot be used here: the context item is an atomic value".
It seems that by using the "1 to n" loop, I am loosing the access to my actual entity. Is there any XPath expression that gets me access back or does anyone have a completely different idea how to realize this?
Here is what I do:
Original XML
<campaigns>
<campaign id="1" name="test">
<campaign id="2" name="another name">
</cmpaigns>
XSLT I try to use
<xsl:template match="/">
<xsl:element name="campaigns">
<xsl:for-each select="1 to 10">
<xsl:for-each select="campaigns/campaign">
<xsl:element name="campaign">
<xsl:copy-of select="#*[local-name() != 'id']" />
<xsl:attribute name="id"><xsl:value-of select="#id" /></xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
</xsl:element>
</xsl:template>
Define a variable as the first thing in the match, like so:
<xsl:variable name="foo" select="."/>
This defines a variable $foo of type nodeset. Then access it like this
<xsl:for-each select="$foo/campaigns/campaign">
...
</xsl:for-each>

Conditional position-check in XSLT

I have an XSLT file which renders articles. I'm passing a variable into the XSLT file which is supposed to limit the number of records output. The trouble is, this 'limit' variable is optional - if it's not there the XSLT file needs to output all values; if the 'limit' variable is a number, then it needs to only output that number of items (presumably using a position() test).
Here's my code:
<xsl:variable name="limit" select="/macro/limit"/>
<xsl:template match="/">
<xsl:variable name="allNodes" select="$localSiteRoot/descendant-or-self::*[articles != '']"/>
<xsl:for-each select="$allNodes">
<div class="articleItem">
<h3><xsl:value-of select="./articleHeader"/></h3>
<xsl:if test="./articleSubheader != ''">
<h4><xsl:value-of select="./articleSubheader"/></h4>
</xsl:if>
</div>
</xsl:for-each>
</xsl:template>
Clearly I could just do an XSLT choose around this and say
<xsl:choose>
<xsl:when select="$limit!= ''">
<xsl:if test="position() < $limit">
But then I'd need to repeat the code twice. There must be a better way, perhaps using templates, but I just can't figure out how it would work and my XSLT isn't great.
Could anyone point me in the right direction with the best/neatest way to approach this?
Thanks!
Just use:
<xsl:for-each select="$allNodes[not(position() > $limit)]">
Explanation:
If the value of $limit is castable to a number, then the expression in the select attribute above selects not more than $limit nodes.
If the value of $limits isn't castable to a number (e.g. empty or 'abc') then it is converted to NaN. By definition comparisons involving NaN are always false(), therefore the predicate not(position() > $limit is true() and in this case all nodes in $allNodes are selected.