Skip to content

Instantly share code, notes, and snippets.

@line-o
Last active February 10, 2023 09:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save line-o/5e01ec6ed4e6efd3c2c2ed03c0255c5a to your computer and use it in GitHub Desktop.
Save line-o/5e01ec6ed4e6efd3c2c2ed03c0255c5a to your computer and use it in GitHub Desktop.
Validate ORCIDs (both format and checksum)
xquery version "3.1";
module namespace orcid="orcid/validate";
declare variable $orcid:R := 2;
declare variable $orcid:M := 11;
declare variable $orcid:format := '^(\d{4}-\d{4}-\d{4}-\d{3}|\d{15})[0-9X]$';
declare variable $orcid:is-valid-format := matches(?, $orcid:format);
declare variable $orcid:expected-sum := map{
'1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'X':10
};
declare function orcid:reduce-digits ($res as xs:integer, $next as xs:integer) as xs:integer {
($res + $next) * $orcid:R mod $orcid:M
};
declare function orcid:checksum ($digits as xs:integer+) as xs:integer {
let $intermediate-result := fold-left($digits, 0, orcid:reduce-digits#2)
return ($orcid:M + 1 - $intermediate-result) mod $orcid:M
};
declare function orcid:validate-checksum ($id-without-dashes as xs:string) as xs:boolean {
let $actual :=
substring($id-without-dashes, 1, 15)
=> local:to-integer-sequence()
=> orcid:checksum()
let $expected :=
substring($id-without-dashes, 16, 1)
=> $orcid:expected-sum()
return (
$actual eq $expected or
error(
xs:QName('orcid:invalid-checksum'),
"The checksum did not match: expected " || $expected || " got " || $actual || ".",
($expected, $actual))
)
};
declare function local:to-integer-sequence ($id-part as xs:string) as xs:integer+ {
string-to-codepoints($id-part) ! (. - 48)
};
declare function orcid:validate-format ($id as xs:string) as xs:string {
if (not($orcid:is-valid-format($id)))
then error(xs:QName('orcid:invalid-format'),
"The provided ID does not match the required format '" || $orcid:format || "'.")
else $id
};
declare function orcid:validate ($id as xs:string) as xs:boolean {
orcid:validate-format($id)
=> replace('-', '')
=> orcid:validate-checksum()
};
xquery version "3.1";
import module namespace orcid="orcid/validate" at "orcid.xqm";
let $inputs := (
(: valid :)
'0000-0001-7414-8743',
'0000-0003-1120-0254',
'0000-0001-9469-3634',
(: valid, with X :)
'0000-0001-8678-347X',
(: valid, no-dashes :)
'0000000194693634',
(: invalid :)
'0400-0001-9469-3634',
(: invalid, no-dashes :)
'0003000194693634',
(: invalid, random :)
'12315',
'wizard'
)
return
<checks>
{
$inputs ! (
try {
<check id="{.}" valid="{ orcid:validate(.) }" />
}
catch orcid:* {
<check id="{.}" valid="false"
code="{ $err:code }"
reason="{ $err:description }" />
})
}
</checks>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment