【问题标题】:sum of alternate numbers (XML/XSL)替代数字的总和 (XML/XSL)
【发布时间】:2009-08-15 03:28:55
【问题描述】:

需要在使用 XSLT 从 XML 文件接收的数字中添加替代数字,例如,如果我收到 123456789,我需要使用 XSLT 函数从最右边计算替代数字总和,我对此有什么建议吗?

谢谢, Laxmikanth.S

【问题讨论】:

    标签: xml xslt xpath


    【解决方案1】:

    这在 XSLT 2.0 中非常容易做到(实际上只需要一个 XPath 2.0 表达式):

    以下 XSLT 转换:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    >
        <xsl:output method="text"/>
    
        <xsl:template match="/">
          <xsl:sequence select=
          "sum(for $n in reverse(string-to-codepoints('123456789'))
                                         [position() mod 2 eq 1]
                 return
                   $n  - string-to-codepoints('0') 
               )
          "/>
        </xsl:template>
    </xsl:stylesheet>
    

    当应用于任何 XML 文档(未使用)时,会产生正确的结果:

    25

    注意 XPath 2.0 函数的使用:string-to-codepoints()codepoints-to-string()reverse()


    更新: 一个类似但更简单的表达式是:

    sum(for $n in reverse(string-to-codepoints('123456789'))
                                     [position() mod 2 eq 1]
          return
             xs:integer(codepoints-to-string($n))
        )
    

    为了编译这个表达式,xs 前缀必须绑定到命名空间:“http://www.w3.org/2001/XMLSchema

    【讨论】:

      【解决方案2】:

      以防万一您这样做是因为您需要计算某事的 Luhn (mod 10) checksum,这已发布在 IBM developerWorks

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:tns="http://www.example.org/tns" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp" exclude-result-prefixes="dp regexp" xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
        <xsl:template match="/">
          <xsl:call-template name="recLuhn">
            <xsl:with-param name="index" select="1"/>
            <xsl:with-param name="parity" select="string-length(CreditCard) mod 2"/>
          </xsl:call-template>
        </xsl:template>
        <xsl:template name="recLuhn">
          <xsl:param name="index"/>
          <xsl:param name="parity"/>
          <xsl:choose>
            <xsl:when test="$index &lt;= string-length(CreditCard) ">
              <xsl:variable name="sum">
                <xsl:call-template name="recLuhn">
                  <xsl:with-param name="index" select="$index + 1"/>
                  <xsl:with-param name="parity" select="$parity"/>
                </xsl:call-template>
              </xsl:variable>
              <xsl:variable name="thisSum">
                <xsl:choose>
                  <xsl:when test="$index mod 2 != $parity">
                    <xsl:choose>
                      <xsl:when test="substring(CreditCard, $index, 1)*2 &gt; 9">
                        <xsl:value-of select="(number(substring(CreditCard, $index, 1)) * 2) - 9"/>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:value-of select="number(substring(CreditCard, $index, 1)) * 2"/>
                      </xsl:otherwise>
                    </xsl:choose>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:value-of select="number(substring(CreditCard, $index, 1))"/>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:variable>
              <xsl:value-of select="$thisSum + $sum"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="0"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:template>
      </xsl:stylesheet>
      

      【讨论】:

        【解决方案3】:

        对于 XSLT 1.0 解决方案,我会使用 FXSL

        此转换首先使用“str-reverse”模板反转字符串,然后对“str-foldl”模板执行两次调用并完成工作:

        <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:dvc-foldl-func="dvc-foldl-func"
        exclude-result-prefixes="xsl dvc-foldl-func"
        >
           <xsl:import href="str-foldl.xsl"/>
           <xsl:import href="strReverse.xsl"/>
        
           <dvc-foldl-func:delEven/>
           <dvc-foldl-func:add/>
        
           <xsl:variable name="vFoldlDelEven" 
                select="document('')/*/dvc-foldl-func:delEven[1]"/>
           <xsl:variable name="vFoldlAdd" 
                select="document('')/*/dvc-foldl-func:add[1]"/>
        
            <xsl:output method="text"/>
        
            <xsl:template match="/">
               <xsl:variable name="vReversed">
                 <xsl:call-template name="strReverse">
                   <xsl:with-param name="pStr" select="'123456789'"/>
                 </xsl:call-template>
               </xsl:variable>
        
               <xsl:variable name="vOddDigits">
                  <xsl:call-template name="str-foldl">
                    <xsl:with-param name="pFunc" select="$vFoldlDelEven"/>
                    <xsl:with-param name="pStr" select="$vReversed"/>
                    <xsl:with-param name="pA0" select="''"/>
                  </xsl:call-template>
              </xsl:variable>
        
              <xsl:call-template name="str-foldl">
                <xsl:with-param name="pFunc" select="$vFoldlAdd"/>
                <xsl:with-param name="pStr" select="$vOddDigits"/>
                <xsl:with-param name="pA0" select="0"/>
              </xsl:call-template>
            </xsl:template>
        
            <xsl:template match="dvc-foldl-func:add">
                 <xsl:param name="arg1" select="0"/>
                 <xsl:param name="arg2" select="0"/>
        
                 <xsl:value-of select="$arg1 + $arg2"/>
            </xsl:template>
        
            <xsl:template match="dvc-foldl-func:delEven">
                 <xsl:param name="arg1"/>
                 <xsl:param name="arg2"/>
        
                 <xsl:copy-of select=
                   "concat($arg1, 
                           $arg2 * (string-length($arg1) mod 2 = 0)
                          )
                   "/>
            </xsl:template>
        </xsl:stylesheet>
        

        当上述转换应用于任何源 XML(忽略)时,得到想要的结果

        25

        请注意

        1. 第一个模板调用反转字符串。
        2. 第二个模板调用将反转字符串的每个偶数位替换为 0。
        3. 最后一个模板调用产生由第二个模板调用产生的字符串中所有数字的总和

        【讨论】:

          【解决方案4】:

          如果您有 EXSL 扩展 (http://exslt.org),您可以执行以下操作:

          sum(str:tokenize($x,'')[position() mod 2 = 0])
          

          如果不是,你可以通过使用 substring 函数循环来做类似的事情。

          【讨论】:

            【解决方案5】:

            如果您不能使用 XSLT 2.0,仍然可以使用 XSLT 1.0。一种方法是递归调用命名模板:

            <xsl:template match="number">
                <xsl:call-template name="sumdigits">
                    <xsl:with-param name="number" select="." />
                </xsl:call-template>
            </xsl:template>
            
            <xsl:template name="sumdigits">
                <xsl:param name="number" />
                <xsl:param name="runningtotal">0</xsl:param>
                <xsl:choose>
                    <xsl:when test="string-length($number) = 0">
                        <xsl:value-of select="$runningtotal" />
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:variable name="lastdigit" select="substring($number, string-length($number), 1)" />
                        <xsl:variable name="newrunningtotal" select="number($runningtotal) + number($lastdigit)" />
                        <xsl:call-template name="sumdigits">
                            <xsl:with-param name="number" select="substring($number, 1, string-length($number) - 2)" />
                            <xsl:with-param name="runningtotal" select="$newrunningtotal" />
                        </xsl:call-template>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:template>
            

            它的工作原理是获取输入中的最后一位数字,将其添加到“运行总数”中,然后递归调用命名模板,并删除输入的最后两位数字。当没有剩余数字时,可以返回累计。

            此 XSLT 片段假定输入位于名为“number”的元素内的 XML 中

               <number>123456789</number>
            

            结果应该是 25。如您所见,在 XSLT2.0 中执行会好得多。

            【讨论】:

              【解决方案6】:

              这是Luhn check digit 生成算法的另一种实现,在 XSLT 2 和 XQuery 中,以 @Dimitre's approach 为基础,用于反转和过滤偶数/奇数。

              (反转的)奇数位的处理被预先映射到一个数组中,然后被替换。

              另请注意,最后一步可以完成(等效):

              • Sum 的 10 的补码
              • (Sum * 9) 模 10
                <xsl:variable name="luhnIndexMap" as="element()*">
                  <Item>0</Item><!--0 * 2 = 0-->
                  <Item>2</Item><!--1 * 2 = 2-->
                  <Item>4</Item><!--2 * 2 = 4-->
                  <Item>6</Item><!--3 * 2 = 6-->
                  <Item>8</Item><!--4 * 2 = 8-->
                  <Item>1</Item><!--5 * 2 = 10. 1 + 0 = 1-->
                  <Item>3</Item><!--6 * 2 = 12. 1 + 2 = 3-->
                  <Item>5</Item><!--7 * 2 = 14. 1 + 4 = 5-->
                  <Item>7</Item><!--8 * 2 = 16. 1 + 6 = 7-->
                  <Item>9</Item><!--9 * 2 = 18. 1 + 8 = 9-->
                </xsl:variable>
              
                <xsl:function name="my:getLuhnCheckDigit" as="xs:string">
                  <xsl:param name="digits" as="xs:string"/>
              
                  <xsl:variable name="reversedDigits" select="reverse(string-to-codepoints($digits))"></xsl:variable>
                  <xsl:variable name="codePoint0" 
                       select="string-to-codepoints('0')"></xsl:variable>
                  <xsl:variable name="sumEvens" 
                       select="sum(for $n in $reversedDigits[position() mod 2 eq 0] return $n - $codePoint0)"></xsl:variable>
                  <xsl:variable name="sumMappedOdds" 
                       select="sum(for $n in $reversedDigits[position() mod 2 eq 1] return $luhnIndexMap[$n - $codePoint0 + 1])"></xsl:variable>
                  <xsl:variable name="luhnCheckDigit" 
                       select="(($sumEvens + $sumMappedOdds) * 9) mod 10"></xsl:variable>
              
                  <xsl:value-of select="$luhnCheckDigit"></xsl:value-of>
                </xsl:function>
              

              注意事项

              • 将函数放置在合适的命名空间中
              • 像这样调用它:
              <xsl:value-of select="my:getLuhnCheckDigit('448508566208738')"></xsl:value-of>
              

              返回 3

              【讨论】:

                【解决方案7】:

                是的;不要为此使用 XSLT 或 XML,这没有意义。

                【讨论】:

                • 虽然是定长数字,我是说10位数字?
                • 我正在使用一个 saxon 解析器,它有 saxon 扩展,但我是这个解析器的新手,有什么建议吗?
                • 我已经提供了。也许其他人会为你准备一些 cmets。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-02-16
                • 2019-06-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-11-30
                • 2011-02-14
                相关资源
                最近更新 更多