3

is it possible to write an XSLT function which can do the basic IBAN Mod-97 check?

From Wikipedia:
1. Move the four initial characters to the end of the string.
2. Replace each letter in the string with two digits, thereby expanding the string, where A=10, B=11, ..., Z=35.
3. Interpret the string as a decimal integer and compute the remainder of that number on division by 97.

If the remainder is 1, the checks digits test is passed and the IBAN may be valid.

Thanks.

oneone
  • 31
  • 2

2 Answers2

4

This transformation:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:my="my:my">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:variable name="vCaps" select=
     "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

 <xsl:template match="text()">
  <xsl:sequence select="my:isIBAN(.)"/>
 </xsl:template>

 <xsl:function name="my:isIBAN" as="xs:boolean">
  <xsl:param name="pString" as="xs:string"/>

  <xsl:variable name="vDigits" select=
   "string-join(
                (for $vStarting4 in substring($pString, 1,4),
                     $vRest in substring($pString, 5),
                     $vNewString in concat($vRest,$vStarting4),
                     $vLen in string-length($vNewString),
                     $i in 1 to $vLen
                   return
                     my:code(substring($vNewString,$i,1))
                 ),
                 ''
                 )
   "/>

   <xsl:sequence select="xs:integer($vDigits) mod 97 eq 1"/>
 </xsl:function>

 <xsl:function name="my:code" as="xs:string">
  <xsl:param name="pChar" as="xs:string"/>

  <xsl:sequence select=
  "if(string-length($pChar) ne 1 or not(contains($vCaps, $pChar)))
     then $pChar
     else
      xs:string
         (string-to-codepoints($pChar) - string-to-codepoints('A') +10)
  "/>
 </xsl:function>
</xsl:stylesheet>

when applied on this XML document:

<t>GB82WEST12345698765432</t>

produces the wanted, correct result:

true

Do note: The function my:isIBAN() can be implemented as a single XPath 2.0 expression. I didn't provide it for reasons of readability.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • +1 Good answer. (It looks like we both have read the same Wiki articule, ja!) –  Jan 07 '11 at 15:53
2

Just for fun (I didn't see that @Dimitre have already answered it)

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:local="http://localhost/">
    <xsl:template match="IBAN[local:validate(.)]">
        <xsl:text>Validated IBAN</xsl:text>
    </xsl:template>
    <xsl:function name="local:validate">
        <xsl:param name="pIBAN" as="xs:string"/>
        <xsl:sequence select="xs:integer(
                                 codepoints-to-string(
                                    for $n in string-to-codepoints(
                                                 concat(
                                                    substring(
                                                       $pIBAN,
                                                       5
                                                    ),
                                                    substring(
                                                       $pIBAN,
                                                       1,
                                                       4
                                                    )
                                                 )
                                              )
                                    return if ($n > 64)
                                           then string-to-codepoints(
                                                   string(
                                                      $n - 55
                                                   )
                                                )
                                           else $n
                                 )
                              ) mod 97 eq 1"/>
    </xsl:function>
</xsl:stylesheet>

With this input:

<IBAN>GB82WEST12345698765432</IBAN>

Output:

Validated IBAN

EDIT: Better one liner.

  • +1 for a good answer. I intentionally didn't produce a single expression -- for better readability. – Dimitre Novatchev Jan 07 '11 at 16:51
  • @Dimitre: I like your XPath 2.0 "variable declaration" inside `for` expression, by the way... ;) –  Jan 07 '11 at 17:06
  • @Alejandro: Yes, this is my basic weapon in writing almost any XSLT 2.0 as (usually single) XPath expression(s). The only case you still must use `` is when the value for the variable is a sequence. In XPath 3.0 this limitation dies as it allows to use the `let` clause in XPath. – Dimitre Novatchev Jan 07 '11 at 17:09