开发者

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 do for-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(. &lt; ../../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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜