【问题标题】:XSLT : Getting max value out of derived values from source XML nodesXSLT:从源 XML 节点的派生值中获取最大值
【发布时间】:2011-01-13 20:34:49
【问题描述】:

我有一个如下的 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 解决方案,它可以在不使用节点集扩展函数的情况下将 Max 值作为转换结果返回给我。

这是我的测试用例

测试用例 1:

输入:

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

预期输出 3

测试用例 2:

输入:

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

预期输出 3

测试用例 3:

输入:

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

预期输出 2

测试用例 4:

输入:

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

预期输出 3

测试用例 5:

输入:

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

预期输出 1

提前致谢。

来自 cmets 的更新

我的查找信息不是 可加载 [with document() 功能]。我需要做for-each 类别输入,然后导出 查找值。之后,我需要 获得最大值。

我有一个可从 xsl 获得的扩展 引擎处理器来得到这个 xslt 如下所示:

<xsl:value-of select='xx:lookupValue("MappingXML","Category",.,"COL1")'/>

此函数返回字符串。这个 函数不返回节点集。我 通过捕获所有变量来尝试 执行后的派生值 for-each,但要进一步处理 此变量输出 (RTF),在 XSLt 中 1.0,我没有处理任何 node-set() 函数。

【问题讨论】:

  • 好问题,+1。请参阅我的答案以获得完整的解决方案。 :)

标签: xml xslt


【解决方案1】:

来自 OP 的评论

我有一个可从 xsl 获得的扩展 引擎处理器来得到这个 xslt 如下所示:<xsl:value-of select='xx:lookupValue("MappingXML","Category",.,"COL1")'/> ——satish

这适用于 XSLT 2.0,并且必须适用于 XSLT 1.0(只需删除 &lt;xsl:function&gt; 并使用您的扩展功能):

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

【讨论】:

  • 这很好用。感谢您的解决方案。顺便说一句,你能解释一下 thuis 线的作用吗?
  • 嗨,我正在尝试过滤传递给模板的节点,但它失败了。这里有什么错误?类别/猫[文本()='音频'或'视频']。如果我只有 Categories/cat[text()='Audio'],它就可以正常工作。请帮忙。我也试过 Categories/cat[text() = 'Audio' 或 text()='Video'],但没有运气。
  • @satish:这个 XPath 表达式返回两个值中较大的一个。它使用了 true() 在 XPath 1.0 中自动转换为 1false()0 的事实。
  • @satish:使用:&lt;xsl:with-param name="pNodes" select="cat[. = 'Audio' or . = 'Video']"/&gt;
  • 谢谢,这个表达式可以很好地过滤。很抱歉再次问您,请您详细说明 ($pcurrMax >= $vNewVal)*$pcurrMax + ($vNewVal > $pcurrMax)*$vNewVal。 * 是乘数吗?然后为什么会有一个+
【解决方案2】:

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

【讨论】:

  • 感谢您的解决方案。它就像魅力一样。如果我有更多 ,这种方法是否会对性能产生影响?有没有办法从性能的角度进一步优化?
  • @satish:这只是一个额外的电话。因此,cat 元素越多,对性能的影响就越小。
【解决方案3】:
<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

【讨论】:

  • 感谢您的快速解决方案。虽然,我无法像您在此处那样加载查找信息。我需要对输入的类别进行 for-each 操作,然后得出查找值。在那之后,我需要得到最大值。您能否修改您的解决方案以支持这一点?
  • 如果不能通过 document() 获取,除了从外部文件中读取任何信息外,您如何?还是查找信息在同一个文件中?
  • 我有一个可从 xsl 引擎处理器获得的扩展,可以在 xslt 中使用,如下所示:
  • 您应该在问题中指出这一点。这个函数返回什么数据类型?
  • 很抱歉没有在原始问题中提及。该函数返回字符串。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多