How to create a composite key for muenchian grouping - xslt

These are just three records of my client's xml, but it shows the problem:
<Export>
<Record>
<JournalX>01-252-5390</JournalX>
<PositionX></PositionX>
<DepartmentX></DepartmentX>
<Description><![CDATA[UK - Take The Cash]]></Description>
<Amount>116.66</Amount>
<PayDate>06/30/2022</PayDate>
<PPEndDate>06/30/2022</PPEndDate>
</Record>
<Record>
<JournalX>5200</JournalX>
<PositionX></PositionX>
<DepartmentX>262</DepartmentX>
<Description><![CDATA[UK - Salary]]></Description>
<Amount>3655.92</Amount>
<PayDate>06/30/2022</PayDate>
<PPEndDate>06/30/2022</PPEndDate>
</Record>
<Record>
<JournalX>5200</JournalX>
<PositionX>311</PositionX>
<DepartmentX>310</DepartmentX>
<Description><![CDATA[UK - Salary]]></Description>
<Amount>1424.99</Amount>
<PayDate>06/30/2022</PayDate>
<PPEndDate>06/30/2022</PPEndDate>
</Record>
</Export>
What needs to happen is to sum Amount by Description and AcctNo. As you can see there is not AcctNo in the XML. This is how I created an AcctNo variable:
<xsl:variable name="AcctNo">
<xsl:choose>
<xsl:when test="string-length(JournalX) > 4">
<xsl:value-of select="JournalX" />
</xsl:when>
<xsl:when test="string-length(PositionX) != 3 or PositionX = '' ">
<xsl:value-of select="concat('01-', DepartmentX, '-', JournalX)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('01-', PositionX, '-', JournalX)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Alternatively, I created an AcctNo function in the CData section, basically the same algorithm. But the problem with either of these, is that I have to create the Key outside the template definition and the variable is defined within the template, therefore not available to the variable.
An additional problem, at least I think it may be, is that they want the output to be csv.
Can anyone help me? Thank You so much, Greg
I have copied code I found on the web and tried to modify it which is how I found out about the limitation.
Any help would be really appreciated.

Using XSLT 3 with
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="text" indent="yes" html-version="5"/>
<xsl:template match="/Export">
<xsl:for-each-group select="Record" composite="yes" group-by="Description, if (string-length(JournalX) > 4) then JournalX else if (string-length(PositionX) != 3 or PositionX = '') then concat('01-', DepartmentX, '-', JournalX) else concat('01-', PositionX, '-', JournalX)">
<xsl:value-of select="sum(current-group()/Amount)"/>
<xsl:text>
</xsl:text>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
I get
116.66
3655.92
1424.99
using XSLT 1.0 with a two step transformation I get the same result
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl"
version="1.0">
<xsl:output method="text" indent="yes" />
<xsl:key name="group-by-desc-and-acctno" match="Record" use="concat(Description, '|', AcctNo)"/>
<xsl:template match="/Export">
<xsl:variable name="records-with-added-acctno-rtf">
<xsl:apply-templates select="Record"/>
</xsl:variable>
<xsl:variable name="records-with-added-acctno" select="exsl:node-set($records-with-added-acctno-rtf)/Record"/>
<xsl:for-each select="$records-with-added-acctno[generate-id() = generate-id(key('group-by-desc-and-acctno', concat(Description, '|', AcctNo))[1])]">
<xsl:value-of select="sum(key('group-by-desc-and-acctno', concat(Description, '|', AcctNo))/Amount)"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="Record">
<xsl:copy>
<xsl:copy-of select="*"/>
<AcctNo>
<xsl:choose>
<xsl:when test="string-length(JournalX) > 4">
<xsl:value-of select="JournalX" />
</xsl:when>
<xsl:when test="string-length(PositionX) != 3 or PositionX = '' ">
<xsl:value-of select="concat('01-', DepartmentX, '-', JournalX)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('01-', PositionX, '-', JournalX)"/>
</xsl:otherwise>
</xsl:choose>
</AcctNo>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT to handle quotes and Pipe Delimited symbol

Experts, i need to write XSLT 1.0 code to remove the quotes for multiple conditions.
CASE1: Remove the double quotes
CASE2: Remove the double quotes + delete the PIPE symbol inside that double quotes (IF exist)
CASE3: Remove Single quote " from the input field.
Input:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ns0:Accounting xmlns:ns0="http://sample.com">
<Record>
<DRCR>"DR"</DRCR>
<GLREFERENCE>"TEST|CASE"</GLREFERENCE>
<GLVALUEDATE>EXAM"PLE</GLVALUEDATE>
<GLACCOUNTNUMBER>"1160</GLACCOUNTNUMBER>
<GLEXAMPLE>123</GLEXAMPLE>
<GLEXAMPLE1>EXTRACT|2021-06-16|2853|1308026.7500|1176</GLEXAMPLE1>
</Record>
</ns0:Accounting>
** Desired Output:**
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ns0:Accounting xmlns:ns0="http://sample.com">
<Record>
<DRCR>DR</DRCR>
<GLREFERENCE>TEST CASE</GLREFERENCE>
<GLVALUEDATE>EXAMPLE</GLVALUEDATE>
<GLACCOUNTNUMBER>1160</GLACCOUNTNUMBER>
<GLEXAMPLE>123</GLEXAMPLE>
<GLEXAMPLE1>EXTRACT|2021-06-16|2853|1308026.7500|1176</GLEXAMPLE1>
</Record>
</ns0:Accounting>
** XSLT I tried:**
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<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:choose>
<xsl:when test="contains($text, '"')">
<xsl:value-of select="substring-before($text, '"')"/>
<xsl:value-of select="translate(substring-before(substring-after($text, '"'), '"'), '|', '')"/>
<xsl:call-template name="process">
<xsl:with-param name="text" select="substring-after(substring-after($text, '"'), '"')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This XSLT not handling case 3, which has single quote in the input field. Please assist here..
Maybe something like this could work for you:
XSLT 1.0 (+ EXSLT node-set function)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:choose>
<xsl:when test="contains(., '"')">
<xsl:variable name="tokens">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="exsl:node-set($tokens)/token">
<xsl:choose>
<xsl:when test="(position()=1 or position()=last()) and last() > 1">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(., '|', '')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'"'"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:if test="$token">
<token>
<xsl:value-of select="$token"/>
</token>
</xsl:if>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Note that this does not check the parity of the quotation marks. Any vertical bar character that is both preceded and followed by a quotation mark will be removed. For example, an input of:
<EXAMPLE>abc|123"def|456"ghi|789"jkl|012</EXAMPLE>
will be transformed to:
<EXAMPLE>abc|123def456ghi789jkl|012</EXAMPLE>

looping number to get sequence

Input XML:
<root>
<number>4</number>
<format>start1</format>
<!--this could be start0/start1/alpha -->
</root>
My output should be:
If format=start1 Print 1,2,3,4
If format=start0 Print 0,1,2,3
If format=alpha Print A,B,C,D
number of sequential items is equal to value of "number" node
XSLT stub:
<xsl:template match="/">
<xsl:variable name="mynumber" select="number"></xsl:variable>
<xsl:variable name="mysequence">
<xsl:choose>
<xsl:when test="format='start0'">
<xsl:for-each select="$mynumber">
<!--0,1,2,3-->
</xsl:for-each>
</xsl:when>
<xsl:when test="format='start1'">
<xsl:for-each select="$mynumber">
<!--1,2,3,4-->
</xsl:for-each>
</xsl:when>
<xsl:when test="format='alpha'">
<xsl:for-each select="$mynumber">
<!--A, B, C, D-->
</xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$mysequence"></xsl:value-of>
</xsl:template>
Consider the following example:
XML
<root>
<item>
<number>4</number>
<format>start0</format>
</item>
<item>
<number>4</number>
<format>start1</format>
</item>
<item>
<number>4</number>
<format>alpha</format>
</item>
</root>
XSLT 2.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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<sequence>
<xsl:choose>
<xsl:when test="format='start0'">
<xsl:value-of select="for $i in 1 to number return $i - 1" separator=", "/>
</xsl:when>
<xsl:when test="format='start1'">
<xsl:value-of select="for $i in 1 to number return $i" separator=", "/>
</xsl:when>
<xsl:when test="format='alpha'">
<xsl:value-of select="for $i in 1 to number return codepoints-to-string($i + 64)" separator=", "/>
</xsl:when>
</xsl:choose>
</sequence>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<sequence>0, 1, 2, 3</sequence>
<sequence>1, 2, 3, 4</sequence>
<sequence>A, B, C, D</sequence>
</root>
Note that this assumes number will not exceed 26 (at least not when the format is "alpha"); otherwise you will need to use xsl:number to format it as alpha, as shown in the answer by #potame - except it could be more concise:
<xsl:template match="item">
<xsl:variable name="fmt" select="format" />
<sequence>
<xsl:for-each select="1 to number">
<xsl:number value="if ($fmt='start0') then . - 1 else ." format="{if ($fmt='alpha') then 'A' else '0'}"/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</sequence>
</xsl:template>
Here's a possible solution, thanks to the use of an XPath sequence, e.g. select="1 to 10":
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="html" doctype-public="XSLT-compat"
omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="root">
<xsl:variable name="mynumber" select="number" as="xs:integer" />
<xsl:variable name="mysequence">
<xsl:choose>
<xsl:when test="format='start0'">
<xsl:for-each select="0 to ($mynumber - 1)">
<!--0,1,2,3-->
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:when>
<xsl:when test="format='start1'">
<xsl:for-each select="1 to $mynumber">
<!--0,1,2,3-->
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:when>
<xsl:when test="format='alpha'">
<xsl:for-each select="1 to $mynumber">
<xsl:number value="." format="A"/>
</xsl:for-each>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$mysequence"/>
</xsl:template>
</xsl:transform>

xslt dynamic / conditional apply-template in function of a variable?

I want to display two different XSLT transformations in function of what the user want. The entire XSL file is the same, except for one line.
This line should be as it
<xsl:template match="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1])]">
or as it
<xsl:template match="/data/peptides/peptide">
My first idea was to create two different .xsl files, and to apply them (javascript) in function of a variable value.
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslDoc);
xhtml = xsltProcessor.transformToFragment(xmlDoc,document);
However, it is just a line and I would like to maintain only one file. I would like to do something like this
<xsl:param name="variable"/>
<xsl:choose>
<xsl:when test="$variable = 0">
<xsl:template match="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1])]">
...
</xsl:template>
</xsl:when>
<xsl:otherwise>
<xsl:template match="/data/peptides/peptide">
...
</xsl:template>
</xsl:otherwise>
</xsl:choose>
But it does not work.
trying with the feature "mode" in the xsl:apply-templates, this code neither works
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
<xsl:param name="analysis" select="1"/>
<xsl:template match="/">
<root><name><xsl:value-of select="$analysis"/></name><xsl:apply-templates select="/data/proteins/protein"/></root>
</xsl:template>
<xsl:template match="/data/proteins/protein">
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]"/>
</xsl:template>
<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:apply-templates select="/data/peptides/peptide" mode="one"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="two"/>
</xsl:otherwise>
</xsl:choose>
<xsl:template match="/data/peptides/peptide" mode="one">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="/data/peptides/peptide[generate-id()=
generate-id(key('byAccSeq', concat(accession, '|', sequence))[1])]" mode="two">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
-> http://www.xsltcake.com/slices/sgWUFu/2
this code is not correct since xsl:choose cannot be a child of xsl:stylesheet
SOLVED (improved below), this is the code that makes what I wanted
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
<xsl:param name="analysis" select="1"/>
<xsl:template match="/">
<root>
<name>
<xsl:value-of select="$analysis"/>
</name>
<xsl:apply-templates select="/data/proteins/protein"/>
</root>
</xsl:template>
<xsl:template match="/data/proteins/protein">
<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="one"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="two"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/data/peptides/peptide" mode="one">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="/data/peptides/peptide" mode="two"/>
<xsl:template match="/data/peptides/peptide[generate-id()=
generate-id(key('byAccSeq', concat(accession, '|', sequence)))]" mode="two">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
IMPROVED: the final code is much easier to read with much less duplicated code
here
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="analysis" select="0"/>
<xsl:key name="byAcc" match="/data/peptides/peptide" use="accession" />
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
<xsl:template match="/">
<root>
<name>
<xsl:value-of select="$analysis"/>
</name>
<xsl:apply-templates select="/data/proteins/protein" />
</root>
</xsl:template>
<xsl:template match="/data/proteins/protein">
<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:apply-templates select="key('byAcc',accession)" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="key('byAcc',accession)[
generate-id()
=
generate-id(key('byAccSeq', concat(accession, '|', sequence)))]" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/data/peptides/peptide">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
I keep the apply-template calls because I need it, but without them (as in the original code, see comments) is even simpler.
Thanks again, not only for answering but for teaching XSLT :)
/data/peptides/peptide[
generate-id()
=
generate-id(
key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1]
)
]
and
/data/peptides/peptide[
generate-id()
=
generate-id(
key('byAccSeq', concat(protein_accessions/accession, '|', sequence))
)
]
are equivalent. generate-id() always returns the ID of the first node in the node-set that has been passed to it.
Therefore it does not make any difference whether you use generate-id(some-node-set) or generate-id(some-node-set[1]). You can use either one of the XSLT files.
Change from <xsl:template> to <xsl:apply-templates>
<xsl:apply-templates select="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1])]" mode="type1" />
<xsl:apply-templates select="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence)))]" mode="type2" />
After that, write <xsl:template> for both or in one accordingly.
Modify your solution in shorter way:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
<xsl:param name="analysis" select="2"/>
<xsl:template match="/">
<root>
<name>
<xsl:value-of select="$analysis"/>
</name>
<xsl:apply-templates select="data[peptides/peptide/accession=proteins/protein/accession]/peptides" />
</root>
</xsl:template>
<xsl:template match="peptides">
<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:copy-of select="peptide"/>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="peptide[generate-id()=generate-id(key('byAccSeq', concat(accession, '|', sequence)))]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I have tested my code as per your input XML & output. Now I can replicate the same.
On my code, still I can reduce some more lines of code
Replace <xsl:for-each> with below line:-
<xsl:copy-of select="peptide[generate-id()=generate-id(key('byAccSeq', concat(accession, '|', sequence)))]"/>
Check it out..

XSL choose when $variable = $variable

I'm trying to check if two XSL variables are the same:
<?xml version="1.0" encoding="UTF-8"?>
<files>
<file filename="export.csv" path="/export" active="true" ftp="false">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:variable name="quote">"</xsl:variable>
<xsl:variable name="sepstart" select="','"/>
<xsl:template match="/">
<xsl:text>"CustomerID"</xsl:text>
<xsl:variable name="second_check">0</xsl:variable>
<xsl:for-each select="orders/order">
<xsl:for-each select="items/item">
<xsl:variable name="output">
<xsl:variable name="first_check"><xsl:value-of select="normalize-space(../../increment_id)"/></xsl:variable>
<xsl:choose>
<xsl:when test="$first_check = $second_check">
Do something...
</xsl:when>
<xsl:otherwise>
Do something...
<xsl:variable name="second_check"><xsl:value-of select="normalize-space(../../increment_id)"/></xsl:variable>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$output" />
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
</file>
</files>
However, it's just skipping over both and not outputting anything.
Can anyone point out what I'm doing wrong?
You cannot "re-declare" variables in XSL. When you write
<xsl:variable name="second_check">
<xsl:value-of select="normalize-space(../../increment_id)"/>
</xsl:variable>
That has no affect, because of this
<xsl:variable name="second_check">0</xsl:variable>
I can't help you though (yet), because I'm not really sure what you're trying to do

Using max in a variable in XSLT 1.0

can I use max function in a variable in XSLT 1?
I need to find a maximum value inside some nodes and I'll need to call this from more places.
So I tried to create a template:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:essox="urn:essox-scripts">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template name="Field001_max_dluznych_splatek">
<xsl:param name="CrRep"/>
<xsl:variable name="PocetDluznychSplatekSplatky">
<xsl:value-of
select="max($CrRep
/Response
/ContractData
/Installments
/InstDetail
/NrOfDueInstalments)" />
</xsl:variable>
<xsl:variable name="PocetDluznychSplatekKarty">
<xsl:value-of
select="max($CrRep
/Response
/ContractData
/Cards
/CardDetail
/NrOfDueInstalments)" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$PocetDluznychSplatekSplatky
>= $PocetDluznychSplatekKarty">
<xsl:value-of select="$PocetDluznychSplatekSplatky"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$PocetDluznychSplatekKarty"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
In XML Spy I get this error:
Error in XPath expression Unknown
function - Name and number of
arguments do not match any function
signature in the static context -
'max'.
What is wrong?
Thanks a lot,
Peter
Use the well known maximum idiom:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:essox="urn:essox-scripts">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template name="Field001_max_dluznych_splatek">
<xsl:param name="CrRep"/>
<xsl:variable name="PocetDluznychSplatekSplatky">
<xsl:call-template name="maximun">
<xsl:with-param name="pSequence"
select="$CrRep
/Response
/ContractData
/Installments
/InstDetail
/NrOfDueInstalments"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="PocetDluznychSplatekSplatky">
<xsl:call-template name="maximun">
<xsl:with-param name="pSequence"
select="$CrRep
/Response
/ContractData
/Cards
/CardDetail
/NrOfDueInstalments"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$PocetDluznychSplatekSplatky
>= $PocetDluznychSplatekKarty">
<xsl:value-of select="$PocetDluznychSplatekSplatky"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$PocetDluznychSplatekKarty"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="maximun">
<xsl:param name="pSequence"/>
<xsl:for-each select="$pSequence">
<xsl:sort select="." data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note: In a named template for reuse.
There is no max function in XSLT 1.0. You can work around this by sorting your elements in descending order and then taking the value of the first one.
Here's another (slower) way to do it:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="PocetDluznychSplatekSplatky"
select="/test/PocetDluznychSplatekSplatky/val[not(../val > .)][1]" />
<xsl:variable name="PocetDluznychSplatekKarty"
select="/test/PocetDluznychSplatekKarty/val[not(../val > .)][1]" />
<xsl:template match="/">
<xsl:choose>
<xsl:when
test="$PocetDluznychSplatekSplatky >=
$PocetDluznychSplatekKarty">
<xsl:value-of select="$PocetDluznychSplatekSplatky" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$PocetDluznychSplatekKarty" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Sample source document:
<test>
<PocetDluznychSplatekSplatky>
<val>22</val>
<val>3241</val>
<val>13</val>
</PocetDluznychSplatekSplatky>
<PocetDluznychSplatekKarty>
<val>1</val>
<val>3234341</val>
<val>13</val>
</PocetDluznychSplatekKarty>
</test>
Output:
3234341
The XPath in each variable's select looks like this:
/test/PocetDluznychSplatekSplatky/val[not(../val > .)][1]
Or, select the val element having no val siblings with a greater value (i.e. the max).
(Obviously, you'll need to adjust the XPath to fit your source document.)
Note: The sort solution performs much better (assuming an n*log(n) sort implementation). The second approach needs to compare each val to every one of its siblings and is therefore quadratic.