XSLT:从源XML节点派生的值中获取最大值

3
我有一个如下的XML结构:

<Categories>
<cat>Video</cat>
<cat>Audio</cat>
<cat>Hybrid</cat>
</Categories>

这个类别可以在查找XML中使用映射,像这样:
<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>

现在我正在寻找一个XSLT解决方案,该解决方案可以在不使用节点集扩展函数的情况下将最大值作为转换的输出结果返回。
以下是我的测试用例:
测试用例1:
输入:
<Categories>
<cat>Video</cat>
<cat>Audio</cat>
<cat>Hybrid</cat>
</Categories>

"Expected Output 3"
"测试用例2:"
"输入: "
<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>

预期输出1

提前感谢。

评论更新:

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 function 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.


好问题,+1。请查看我的答案以获取完整解决方案。 :) - Dimitre Novatchev
3个回答

2

来自原帖的一条评论:

我有一个从xsl引擎处理器中获取此类内容的扩展,像下面这样在xslt中使用:<xsl:value-of select='xx:lookupValue("MappingXML","Category",.,"COL1")'/> - satish

在XSLT 2.0中可以工作,在XSLT 1.0中必须工作(只需删除<xsl: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>

当应用此转换于提供的XML文档时:

<Categories>
    <cat>Video</cat>
    <cat>Audio</cat>
    <cat>Hybrid</cat>
</Categories>

正确的结果已生成:

3

转换为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>

这个很好用。感谢您提供的解决方案。顺便问一下,您能解释一下这行代码是做什么的吗?<xsl:with-param name="pcurrMax" select="($pcurrMax >= $vNewVal)$pcurrMax+($vNewVal > $pcurrMax)$vNewVal"/> - satish
嗨,我正在尝试过滤传递给模板的节点,但是失败了。这里有什么错误?Categories/cat[text() = 'Audio' or 'Video']。如果我只有Categories/cat[text()='Audio'],它可以正常工作。请帮忙。我还尝试了Categories/cat[text() = 'Audio' or text()='Video'],但没有成功。 - satish
@satish:这个XPath表达式返回两个值中较大的那个。它利用了XPath 1.0自动将true()转换为1,将false()转换为0的特性。 - Dimitre Novatchev
@satish:使用:<xsl:with-param name="pNodes" select="cat[. = '音频' or . = '视频']"/> - Dimitre Novatchev
谢谢,这个表达式可以很好地过滤。抱歉再次问您,您能否更详细地解释一下($pcurrMax >= $vNewVal)$pcurrMax + ($vNewVal > $pcurrMax)$vNewVal?*是乘法符号吗?然后为什么还有一个+? - satish
@satish:是的,在XPath中,*是乘法运算符,+是加法运算符。该表达式表示两个参数的和,其中恰好有一个参数为0。乘法是将值乘以1(true())或0(false())。 - Dimitre Novatchev

1

这是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>

当然,你的限制和映射强制它添加一个额外的调用到扩展函数...如果这是一个很大的代价,你应该选择逐个节点递归(或遍历以下兄弟轴)的解决方案,比如 @Dimitre 提供的方式。
在XPath/XSLT 2.0中更容易:
max(Categories/cat/xx:lookupValue('MappingXML','Category',.,'COL1')

谢谢这个解决方案。它很好地工作了。如果我有更多的<cat>,是否会对性能产生影响?从性能角度来看,有没有进一步优化的方法? - satish
@satish:只是一个额外的调用而已。因此,cat 元素越多,性能影响就越小。 - user357812

0
<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>

结果:

<Categories>
    <cat>Video</cat>
    <cat>Audio</cat>
    <cat>Hybrid</cat>
</Categories>

结果是3

<Categories>
    <cat>Video</cat>
    <cat>Hybrid</cat>
</Categories>

结果是 3

<Categories>
    <cat>Video</cat>
    <cat>Audio</cat>
</Categories>

结果为2

<Categories>
    <cat>Audio</cat>
    <cat>Hybrid</cat>
</Categories>

结果是3

<Categories>
    <cat>Video</cat>
</Categories>

结果是1


我有一个扩展可以从xsl引擎处理器中获取,以便在xslt中像下面这样使用:<xsl:value-of select='xx:lookupValue("MappingXML","Category",.,"COL1")'/>。 - satish
你应该在问题中指出这一点。这个函数返回什么数据类型? - Flack
抱歉在原问题中没有提到。此函数返回字符串。 - satish
好的,你明白我的需求了。这个函数不返回节点集。我尝试使用变量来捕获执行for-each后得到的所有派生值,但是在XSLT 1.0中要进一步处理这个变量输出(RTF),我无法使用任何node-set()函数。 - satish
谢谢您的时间。我会更新问题。有空请检查一下。晚安。 - satish
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接