XSLT : Getting max value out of derived values from source XML nodes
I have an XML structure like below
<Categories>
<cat>Video</cat>
<cat>Audio</cat>
<cat>Hybrid</cat>
</Categories>
There is a mapping available for this categories in a lookup XML like this
<Lookup>
<cat>Video</cat>
<mapping>1</mapping>
</Lookup>
<Lookup>
<cat>Audio</cat>
<mapping>2</mapping>
</Lookup>
<Lookup>
<cat>Hybrid</cat>
<mapping>3</mapping>
</Lookup>
</ValueSet>
Now I am looking for an XSLT solution, which can return me the Max value as output as result of transformation without using node-set extension function.
Here are my Test cases
Test case 1 :
Input :
<Categories>
<cat>Video</cat>
<cat>Audio</cat>
<cat>Hybrid</cat>
</Categories>
Expected Output 3
Test case 2 :
Input :
<Categories>
<cat>Video</cat>
<cat>Hybrid</cat>
</Categories>
Expected Output 3
Test case 3 :
Input :
<Categories>
<cat>Video</cat>
<cat>Audio</cat>
</Categories>
Expected Output 2
Test case 4 :
Input :
<Categories>
<cat>Audio</cat>
<cat>Hybrid</cat>
</Categories>
Expected Output 3
Test case 5 :
Input :
<Categories>
<cat>Video</cat>
</Categories>
Expected Output 1
Thanks in advance.
Update from comments:
Lookup information for me is not available to load [with
document()
function]. I need to dofor-each
on the categories input and then derive the lookup value. After that , I need to get the maximum.I have an extension available from xsl engine processor to get this with in xslt like below:
<xsl:value-of select='xx:lookupValue("MappingXML","Category",.,"COL1")'/>
This function returns string. This 开发者_如何学Gofunction doesn't return nodeset. I tried with variable by capturing all the derived values after executing
for-each
, but to process further this variable output (RTF), in XSLt 1.0, i have no handle on any node-set() functions.
From a comment of the OP:
I have an extension available from xsl engine processor to get this with in xslt like below :
<xsl:value-of select='xx:lookupValue("MappingXML","Category",.,"COL1")'/>
– satish
This works in XSLT 2.0 and must work in XSLT 1.0 (just remove the <xsl:function>
and use your extension function):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xx="my:xx"
>
<xsl:output omit-xml-declaration="yes"/>
<xx:lookup>
<ValueSet>
<Lookup>
<cat>Video</cat>
<mapping>1</mapping>
</Lookup>
<Lookup>
<cat>Audio</cat>
<mapping>2</mapping>
</Lookup>
<Lookup>
<cat>Hybrid</cat>
<mapping>3</mapping>
</Lookup>
</ValueSet>
</xx:lookup>
<xsl:variable name="vlookupDoc" select="document('')/*/xx:lookup"/>
<xsl:template match="/*">
<xsl:call-template name="getMax">
<xsl:with-param name="pNodes" select="cat"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="getMax">
<xsl:param name="pcurrMax" select="-9999999999"/>
<xsl:param name="pNodes"/>
<xsl:choose>
<xsl:when test="not($pNodes)">
<xsl:value-of select="$pcurrMax"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vNewVal" select=
"number(xx:lookupValue($vlookupDoc,$pNodes[1]))"/>
<xsl:call-template name="getMax">
<xsl:with-param name="pNodes" select="$pNodes[position() >1]"/>
<xsl:with-param name="pcurrMax" select=
"number(($pcurrMax >= $vNewVal))*$pcurrMax
+
number(($vNewVal > $pcurrMax))*$vNewVal"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:function name="xx:lookupValue">
<xsl:param name="pLookupDoc"/>
<xsl:param name="pCat"/>
<xsl:value-of select=
"$pLookupDoc/*/*[cat=$pCat]/mapping"/>
</xsl:function>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Categories>
<cat>Video</cat>
<cat>Audio</cat>
<cat>Hybrid</cat>
</Categories>
the wanted, correct result is produced:
3
The code converted to XSLT 1.0:
<xsl:stylesheet version="12.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xx="Your Namespace Here"
>
<xsl:output omit-xml-declaration="yes"/>
<xx:lookup>
<ValueSet>
<Lookup>
<cat>Video</cat>
<mapping>1</mapping>
</Lookup>
<Lookup>
<cat>Audio</cat>
<mapping>2</mapping>
</Lookup>
<Lookup>
<cat>Hybrid</cat>
<mapping>3</mapping>
</Lookup>
</ValueSet>
</xx:lookup>
<!-- You probably don't need this and the above embedded XML -->
<xsl:variable name="vlookupDoc" select="document('')/*/xx:lookup"/>
<xsl:template match="/*">
<xsl:call-template name="getMax">
<xsl:with-param name="pNodes" select="cat"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="getMax">
<xsl:param name="pcurrMax" select="-9999999999"/>
<xsl:param name="pNodes"/>
<xsl:choose>
<xsl:when test="not($pNodes)">
<xsl:value-of select="$pcurrMax"/>
</xsl:when>
<xsl:otherwise>
<!-- Change the call of the ext. function as appr. -->
<xsl:variable name="vNewVal" select=
"number(xx:lookupValue($vlookupDoc,$pNodes[1]))"/>
<xsl:call-template name="getMax">
<xsl:with-param name="pNodes" select="$pNodes[position() >1]"/>
<xsl:with-param name="pcurrMax" select=
"($pcurrMax >= $vNewVal)*$pcurrMax
+
($vNewVal > $pcurrMax)*$vNewVal"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This is the classic maximum algorithm for XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xx="extension-URI">
<xsl:template match="/">
<xsl:for-each select="Categories/cat">
<xsl:sort select="xx:lookupValue('MappingXML',
'Category',
.,
'COL1')"
data-type="number"
order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="xx:lookupValue('MappingXML',
'Category',
.,
'COL1')"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Of course, your restrictions and the mapping force this to add one extra call to the extension function... If this is a big cost, you should go with a recursive node by node (or trasversal on the following sibling axis) solution like @Dimitre.
In XPath/XSLT 2.0 it's easier:
max(Categories/cat/xx:lookupValue('MappingXML','Category',.,'COL1')
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:variable name="docLookup" select="document('lookup_file.xml')/*/Lookup"/>
<xsl:variable name="avElms" select="cat"/>
<xsl:value-of select="$docLookup/mapping
[not(. < ../../Lookup[cat = $avElms]/mapping)]/text()"/>
</xsl:template>
</xsl:stylesheet>
Results:
<Categories>
<cat>Video</cat>
<cat>Audio</cat>
<cat>Hybrid</cat>
</Categories>
Result is 3
<Categories>
<cat>Video</cat>
<cat>Hybrid</cat>
</Categories>
Result is 3
<Categories>
<cat>Video</cat>
<cat>Audio</cat>
</Categories>
Result is 2
<Categories>
<cat>Audio</cat>
<cat>Hybrid</cat>
</Categories>
Result is 3
<Categories>
<cat>Video</cat>
</Categories>
Result is 1
精彩评论