Incrementer in Nested for-each-group Calls - xslt

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>

Related

XSLT discrepancy with how variable is used

My question is about xsl:variable and the syntax for a predicate in an Xpath. I've boiled down my question to the point where this short XML can help me demonstrate:
<root>
<tabular>
<col halign="left"/>
<col halign="right"/>
<row>
<cell>Some content</cell>
<cell>Some content</cell>
</row>
</tabular>
</root>
In my application, when I am applying a template on a cell, I need to access the #halign of the corresponding col. In doing so, I have encountered a discrepancy between Xpath expressions that I thought should be equivalent. I would like to understand why this happens. To demonstrate, I apply the XSL at the end of this post using XSLT 1.0.
The cell template in my XSLT here is silly but it lays out the discrepancy I don't understand. Basically it repeatedly tries to print the #halign value corresponding to the second cell. First, using the $col variable that has value 2. Then using [position()=$col]. Then using [number($col)]. Then simply using [2], hard coded. Lastly, using a separate $colsel variable that was defined using a #select attribute.
I expect to see:
ancestor::tabular/col[...]/#halign
[2] makes right
[position()=2] makes right
[number(2)] makes right
(hard 2) [2] makes right
(var #select) [2] makes right
but instead I see:
ancestor::tabular/col[...]/#halign
[2] makes left
[position()=2] makes right
[number(2)] makes right
(hard 2) [2] makes right
(var #select) [2] makes right
Is anyone able to offer an explanation for why using [$col] behaves differently?
Here is the XSL:
<xsl:template match="/">
<xsl:apply-templates select="root/tabular"/>
</xsl:template>
<xsl:template match="tabular">
<xsl:apply-templates select="row"/>
</xsl:template>
<xsl:template match="row">
<xsl:apply-templates select="cell"/>
</xsl:template>
<?xml version='1.0'?> <!-- As XML file -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates select="root/tabular"/>
</xsl:template>
<xsl:template match="tabular">
<xsl:apply-templates select="row"/>
</xsl:template>
<xsl:template match="row">
<xsl:apply-templates select="cell[2]"/>
</xsl:template>
<xsl:template match="cell[2]">
<xsl:variable name="col">
<xsl:value-of select="2"/>
</xsl:variable>
<xsl:variable name="colsel" select="2"/>
<xsl:text>ancestor::tabular/col[...]/#halign</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text> [</xsl:text>
<xsl:value-of select="$col"/>
<xsl:text>] makes </xsl:text>
<xsl:value-of select="ancestor::tabular/col[$col]/#halign"/>
<xsl:text>
</xsl:text>
<xsl:text> [position()=</xsl:text>
<xsl:value-of select="$col"/>
<xsl:text>] makes </xsl:text>
<xsl:value-of select="ancestor::tabular/col[position()=$col]/#halign"/>
<xsl:text>
</xsl:text>
<xsl:text> [number(</xsl:text>
<xsl:value-of select="$col"/>
<xsl:text>)] makes </xsl:text>
<xsl:value-of select="ancestor::tabular/col[number($col)]/#halign"/>
<xsl:text>
</xsl:text>
<xsl:text>(hard 2) [2] makes </xsl:text>
<xsl:value-of select="ancestor::tabular/col[2]/#halign"/>
<xsl:text>
</xsl:text>
<xsl:text>(var #select) [</xsl:text>
<xsl:value-of select="$colsel"/>
<xsl:text>] makes </xsl:text>
<xsl:value-of select="ancestor::tabular/col[$colsel]/#halign"/>
<xsl:text>
</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Let us use a more convenient example:
XML
<root>
<item>first</item>
<item>second</item>
</root>
XSLT 1.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="num" select="2"/>
<xsl:variable name="str" select="string(2)"/>
<xsl:variable name="rtf">2</xsl:variable>
<xsl:template match="/root">
<results>
<num>
<xsl:copy-of select="item[$num]"/>
</num>
<str>
<xsl:copy-of select="item[$str]"/>
</str>
<rtf>
<xsl:copy-of select="item[$rtf]"/>
</rtf>
</results>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<results>
<num>
<item>second</item>
</num>
<str>
<item>first</item>
<item>second</item>
</str>
<rtf>
<item>first</item>
<item>second</item>
</rtf>
</results>
Now you ask why the difference in the results. The answer can be found in the XPath specification that prescribes how a predicate is to be evaluated:
A PredicateExpr is evaluated by evaluating the Expr and converting the
result to a boolean. If the result is a number, the result will be
converted to true if the number is equal to the context position and
will be converted to false otherwise; if the result is not a number,
then the result will be converted as if by a call to the boolean
function.
In the first instance the value of the $num variable is the number 2. Therefore the result of evaluating the expression within the predicate is a number, and the predicate will be true when the number is equal to the context position - which is only true for the item in the second position.
In the second instance, the value of the $str variable is the string "2". Therefore the expression within the predicate does not evaluate to a number and will be converted to boolean by doing:
boolean("2")
which returns true() for all items, regardless of their position.
In the third instance, the value of the $rtf variable is a result tree fragment that contains a text node that consists of the character "2". When placed in a predicate, the outcome will be similar to the previous instance: the result of evaluating the expression is not a number, and converting it to a boolean will produce a value of true(). Note that your:
<xsl:variable name="col">
<xsl:value-of select="2"/>
</xsl:variable>
does exactly the same thing.
Note also that in XSLT 1.0 the xsl:value-of instruction returns the value of the first node in the selected node-set. Therefore, if we change our template to:
<xsl:template match="/root">
<results>
<num>
<xsl:value-of select="item[$num]"/>
</num>
<str>
<xsl:value-of select="item[$str]"/>
</str>
<rtf>
<xsl:value-of select="item[$rtf]"/>
</rtf>
</results>
</xsl:template>
the result will be:
<results>
<num>second</num>
<str>first</str>
<rtf>first</rtf>
</results>
but still both items are selected by item[$str] and by item[$rtf].
Change the variable declaration to:
<xsl:variable name="col" select="2"/>
and it will behave as you expect and select the second col.
You had declared the variable using xsl:value-of: <xsl:value-of select="2"/>, which creates a computed text() node.
When you use that $col variable by itself in a predicate, that string value "2" it is evaluated as true() in the predicate test, rather than if it were a number() and would then be interpreted as short-hand for position() = 2.

xslt - adding Lp. to node

I'm new in xslt, so I've some problems with adding Lp to my transformation.
This's my simple xml data:
<booking>
<bookingID>ww1</bookingID>
<voucherNumber>R-108</voucherNumber>
</booking>
<booking>
<bookingID>ww2</bookingID>
<voucherNumber>R-108</voucherNumber>
</booking>
<booking>
<bookingID>ww3</bookingID>
<voucherNumber>R-108</voucherNumber>
</booking>
<booking>
<bookingID>ww4</bookingID>
<voucherNumber>R-109</voucherNumber>
</booking>
<booking>
<bookingID>ww5</bookingID>
<voucherNumber>R-109</voucherNumber>
</booking>
<booking>
<bookingID>ww6</bookingID>
<voucherNumber>R-110</voucherNumber>
</booking>
The key is voucherNumber, i need to add Lp for the same voucherNumber
I'need output text file to look like this:
ID;VN,LP
ww1;108;1
ww2;108;2
ww3;108;3
ww4;109;1
ww5;109;2
ww6;110;1
I add the key on voucherNumber
<xsl:key name="x" match="booking" use="voucherNumber"/>
in for-each statement I've add this code: it's adding me on the last position (i know that i can change this for another position) the number of count my items for the same voucherNumber, but how i can add number Lp for the other items?
<xsl:choose>
<xsl:when test="generate-id(.) =generate-id(key('x',voucherNumber)[last()])">
<xsl:value-of select="count(key('x',voucherNumber)) "/>
</xsl:when>
<xsl:otherwise>
-- need LP for other items --
</xsl:otherwise>
</xsl:choose>
I can use only version 1.0 of xslt stylesheet.
Thank you for your advice
Best regards
It looks like you are trying to use Muenchian Grouping here, but what you probably should do is start off by selected the booking elements with the first occurrence of each distinct voucherNumber
<xsl:for-each select="booking[generate-id() = generate-id(key('x',voucherNumber)[1])]">
Then, you have a nested xsl:for-each where you get all the booking elements within that group (i.e. the booking elements with the same voucherNumber)
<xsl:for-each select="key('x', voucherNumber)">
Then, within this next xsl:for-each you can use the position() function to get the count of the record within that specific group
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:key name="x" match="booking" use="voucherNumber"/>
<xsl:template match="/*">
<xsl:for-each select="booking[generate-id() =generate-id(key('x',voucherNumber)[1])]">
<xsl:for-each select="key('x', voucherNumber)">
<xsl:value-of select="bookingID" />
<xsl:text>,</xsl:text>
<xsl:value-of select="substring-after(voucherNumber, '-')" />
<xsl:text>,</xsl:text>
<xsl:value-of select="position()" />
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note, this assumed your actual XML is well-formed and there is a single root element containing all your booking elements.
I have no idea what "Lp" means. Assuming you want to number the bookings sequentially, restarting on voucherNumber, try something like:
-- Edit --
The proper solution here would be to use <xsl:number> to number the nodes. However, since I could not find a single combination of attributes that would work the same way with all XSLT 1.0 processors, I have resorted to the following hack:
<xsl:key name="booking-by-voucherNumber" match="booking" use="voucherNumber"/>
<xsl:template match="/root">
<xsl:for-each select="booking">
<!-- get id and voucher number -->
<xsl:variable name="id" select="generate-id()" />
<xsl:for-each select="key('booking-by-voucherNumber', voucherNumber)">
<xsl:if test="generate-id()=$id">
<xsl:value-of select="position()"/>
</xsl:if>
</xsl:for-each>
<!-- new line -->
</xsl:for-each>
</xsl:template>

How can this XSL be refactored to take advantage of apply-templates?

Taking this question about beautiful XSL but getting more specific, how should I refactor this XSL to take advantage of apply-templates and/or keys.
I tend to "over use" for-each elements to control the context of the source and I can imagine that apply-templates can help. Despite much Google-ing, I still don't understand how to control context within the multiple templates.
In the below example, how can the repetitive XPath segments be reduced by refactoring?
<xsl:template match="/">
<xsl:element name="Body">
<xsl:element name="Person">
<xsl:if test="/source/dbSrc/srv/v[#name='name']/text()='false'">
<xsl:element name="PhoneNumber" />
<xsl:element name="Zip">
<xsl:value-of
select="/source/req[1]/personal-info/address-info/zip-code" />
</xsl:element>
</xsl:if>
<xsl:if test="/source/dbSrc/srv/v[#name='name']/text()='true'">
<xsl:element name="PhoneNumber" />
<xsl:element name="Zip">
<xsl:value-of select="/source/req[3]/personal-info/address-info/zip-code" />
</xsl:element>
</xsl:if>
</xsl:element>
</xsl:template>
One initial way of refactoring the given code would be the following:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Body>
<Person>
<PhoneNumber/>
<Zip>
<xsl:apply-templates select=
"/*/dbSrc/srv/v[#name='name']"/>
</Zip>
</Person>
</Body>
</xsl:template>
<xsl:template match="v[#name='name' and .='true']">
<xsl:value-of select=
"/*/req[3]/personal-info/address-info/zip-code"/>
</xsl:template>
<xsl:template match="v[#name='name' and .='false']">
<xsl:value-of select=
"/*/req[1]/personal-info/address-info/zip-code"/>
</xsl:template>
</xsl:stylesheet>
Do note: The refactored code doesn't contain any conditional xslt instructions.
Further refactoring can let us get rid of the last too templates, because in this case additional templates aren't actually needed -- the code only creates a single element and depends on a single condition:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vCond" select=
"/*/dbSrc/srv/v[#name='name']/text()='true'"/>
<xsl:variable name="vInd" select=
"3*$vCond + 1*not($vCond)"/>
<xsl:template match="/">
<Body>
<Person>
<PhoneNumber/>
<Zip>
<xsl:value-of select=
"/*/req[position()=$vInd]
/personal-info/address-info/zip-code"/>
</Zip>
</Person>
</Body>
</xsl:template>
</xsl:stylesheet>
Note: Here we assume that /*/dbSrc/srv/v[#name='name']/text() can have only two possible values: 'true' or 'false'
In XSLT 2.0 I would write this as:
<xsl:template match="/">
<Body>
<Person>
<PhoneNumber/>
<Zip>
<xsl:variable name="index" as="xs:integer"
select="if (/source/dbSrc/srv/v[#name='name']='true') then 3 else 1"/>
<xsl:value-of select="/source/req[$index]/personal-info/address-info/zip-code"/>
</Zip>
</Person>
</Body>
</xsl:template>
With 1.0, the xsl:variable becomes a bit more complicated but otherwise it's the same.
Note the use of literal result elements and variables to reduce the size of the code; also the avoidance of "/text()", which is nearly always bad practice.
There's very little mileage in using template rules here because you're using so little of the input data, and because you seem to know exactly where to find it. Template rules would come into their own if you wanted to be less rigid about knowing exactly where you are looking in the source: they help to make code more resilient to variability and change in the input. But without seeing the source and knowing more of the background, we can't tell you where that flexibility is needed. The hard-coding of the indexes "1" and "3" looks like a danger signal to me, but only you can judge that.

xsl get array of elements

Hi
I need get array of elements (before "-" if exist) by xsl.
xml is
<Cars>
<Car Trunck="511"/>
<Car Trunck="483-20"/>
<Car Trunck="745"/>
</Cars>
xsl is
<xsl:variable name="testarr">
<xsl:for-each select="//Cars//Car/#Trunck">
<xsl:value-of select="number(substring(.,1,3))" />
</xsl:for-each>
</xsl:variable>
(i suppose that all numbers is three-digit number, if someone knows a solution for all conditions will be glad to hear the proposal)
if i do this
i get all numbers in one line: 511483745
and i need get them in array
because i also need get the max value
thanks
Hi I need get array of elements
(before "-" if exist) [...] i need get
them in array because i also need get
the max value
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Cars/Car/#Trunck">
<xsl:sort select="concat(substring-before(.,'-'),
substring(., 1 div not(contains(.,'-'))))"
data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of
select="concat(substring-before(.,'-'),
substring(.,1 div not(contains(.,'-'))))"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output:
745
XPath 2.0 one line:
max(/Cars/Car/#Trunck/number(replace(.,'-.*','')))
You could use the substring-before and substring-after functions: See the excellent ZVON tutorial
http://zvon.org/xxl/XSLTreference/Output/function_substring-after.html
In your example you are only extracting the values (which are strings) which get concatenated. Perhaps you need to wrap the result in your own element
<xsl:for-each select="//Cars//Car/#Trunck">
<truck>
<xsl:value-of select="number(substring(.,1,3))" />
</truck>
</xsl:for-each>
While you have two good answers (especially that by #Alejandro), here's one from me that I think is even better:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="pTopNums" select="2"/>
<xsl:template match="/*">
<xsl:apply-templates select="*">
<xsl:sort data-type="number" order="descending"
select="substring-before(concat(#Trunck,'-'),'-')"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Car">
<xsl:if test="not(position() > $pTopNums)">
<xsl:value-of select=
"substring-before(concat(#Trunck,'-'),'-')"/>
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document (the originally provided one, slightly changed to be more challenging):
<Cars>
<Car Trunck="483-20"/>
<Car Trunck="311"/>
<Car Trunck="745"/>
</Cars>
produces the wanted, correct result (the top two numbers that are derived from #Trunck as specified in the question):
745
483

Finding max of three numbers in XSL

How can find max of three numbers in XSL ?
More Information : I have three numbers say 1.0, 2.0, 5.0....I don't have any nodes set... I would like to find maximum of 1.0, 2.0 and 5.0.....
Example :
AIM : TO know which type of node <MYNODE>, <DOC>, <PIC>
is having maximum count and whant's the count number ?
<ROOT>
<MYNODES>
<MYNODE>A</MYNODE>
<MYNODE>B</MYNODE>
<MYNODE>C</MYNODE>
<MYNODE>D</MYNODE>
</MYNODES>
<DOCS>
<DOC>1</DOC>
<DOC>2</DOC>
<DOC>3</DOC>
</DOC>
<PICS>
<PIC>a.jpeg</PIC>
<PIC>b.jpeg</PIC>
<PIC>c.jpeg</PIC>
<PIC>d.jpeg</PIC>
<PIC>e.jpeg</PIC>
</PICS>
</ROOT>
With your input XML, you would find the maximum count you are looking for like this:
<xsl:variable name="vMaxChildren">
<xsl:for-each select="/ROOT/*">
<xsl:sort select="count(*)" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="concat(name(), ': ', count(*))" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="$vMaxChildren" />
Which would produce:
PICS: 5
This question is incorrectly formulated and the provided "XML document' is not well-formed!
Do note that it is generally meaningless to ask about the maximum of a set of numbers. There can be more than one number with the highest value. Therefore, the solutions below show just the first item with the maximum value.
This is one possible XSLT 1.0 solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="vNameMaxCount">
<xsl:for-each select="*/*">
<xsl:sort select="count(*)" data-type="number"
order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="concat(name(),'+', count(*))"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
One element with maximum children is: <xsl:text/>
<xsl:value-of select="substring-before($vNameMaxCount, '+')"/>
Maximum number of children: <xsl:text/>
<xsl:value-of select="substring-after($vNameMaxCount, '+')"/>
</xsl:template>
</xsl:stylesheet>
when the above transformation is applied on the following XML document (produced from the one provided after spending 10 minutes to make it well-formed!):
<ROOT>
<MYNODES>
<MYNODE>A</MYNODE>
<MYNODE>B</MYNODE>
<MYNODE>C</MYNODE>
<MYNODE>D</MYNODE>
</MYNODES>
<DOCS>
<DOC>1</DOC>
<DOC>2</DOC>
<DOC>3</DOC>
</DOCS>
<PICS>
<PIC>a.jpeg</PIC>
<PIC>b.jpeg</PIC>
<PIC>c.jpeg</PIC>
<PIC>d.jpeg</PIC>
<PIC>e.jpeg</PIC>
</PICS>
</ROOT>
the wanted result is produced
One element with maximum children is: PICS
Maximum number of children: 5
An XSLT 2.0 solution (actually just an XPath 2.0 soulution):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:sequence select=
"for $vmaxChildrein in max(/*/*/count(*)),
$vmaxNode in */*[count(*) = $vmaxChildrein][1]
return
(name($vmaxNode),
'has the max no of children:',
$vmaxChildrein
)
"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the above document, the wanted result is produced:
PICS has the max no of children: 5
For finding the maximum of more tricky properties that cannot be immediately expressed as an XPath expression and used in <xsl:sort>, do use the f:maximum() function of FXSL.
5.0 is the largest of the three numbers. Hardcode it and be done with it. :-)
Seriously though, you may wish to take another path. Logic like this is trivial in other languages, but can be a pain in XSLT. You should consider writing a simple extension for the XSLT engine rather than messing around trying to make XSLT do what you want it to.