备用数字的总和(XML / XSL)

时间:2009-08-15 03:28:55

标签: xml xslt xpath

需要在使用XSLT从XML文件接收的号码中添加备用数字,例如,如果我收到123456789,我需要使用XSLT函数从最右边计算备用数字总和,我可以对此有任何建议吗? / p>

谢谢, Laxmikanth.S

7 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

如果您这样做是因为您需要计算某些内容的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>

答案 2 :(得分:1)

对于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. 最后一次模板调用产生第二次模板调用产生的字符串中所有数字的总和

答案 3 :(得分:0)

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

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

如果没有,你可以通过使用子串函数循环来做类似的事情。

答案 4 :(得分:0)

如果您无法使用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中执行此操作会更好。

答案 5 :(得分:0)

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

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

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

  • 求和的10的补码
  • (总和* 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

答案 6 :(得分:-2)

是;不要使用XSLT或XML,这没有用。