Parsing an array of dates in XQuery (MarkLogic v8 flavor)
My question seems trivial, but couldn't figure out how to parse a string containing a comma-separated list of dates. Parsing part of individual dates is not a problem, but empty values. The problem is that the order of the dates is significant and some dates can be omitted. Dates are expected to be formatted in YYYY-mm-dd format
So, the following are valid inputs and expected return values:
,2000-12-12,2012-05-03, β ( NULL, 2000-12-12, 2012-05-03, NULL )
2000-12-12,,2012-05-03 β ( 2000-12-12, NULL, 2012-05-03 )
And here is my function signature
declare function local:assert-date-array-param(
$input as xs:string
, $accept-nulls as xs:boolean?
) as xs:date*
I figured out the problem by realizing that there seems to be no NULL equivalent in XQuery for return values ββas placeholders for dropped dates if you want to return a sequence, that is. Because empty sequences wrapped inside sequences are flattened to zero.
I suppose my disclaimer would be to use a date like 1900-01-01 as a placeholder or return a card instead of a sequence, but I'm sure I hope to find a more elegant way
Thank you
K.
PS. I'm working with MarkLogic v8 (and soon v9) and any solution needs to be done with their XQuery processor.
UPDATE: Thanks for both answers, in the end I decided to go with a placeholder date since XQuery works so well with sequences and something else would require some changes elsewhere. But the problem remains in cases where the required return values ββare numbers. In this case, using placeholder values ββwould probably not be possible. A null literal for xs: anyAtomicType could solve the problem nicely, but alas.
source to share
You may consider going back json:array()
or array-node{}
from null-node{}
inside. But perhaps the null placeholder isn't as bad as it sounds:
declare variable $null-date := xs:date("0001-01-01");
declare function local:assert-date-array-param(
$input as xs:string,
$accept-nulls as xs:boolean?
) as xs:date*
{
for $d in fn:tokenize($input, "\s*,\s*")
return
if ($d eq "") then
if ($accept-nulls) then
$null-date
else
fn:error(xs:QName("NULL-NOT-ALLOWED"), "Date is required")
else
if ($d castable as xs:date) then
xs:date($d)
else if ($d castable as xs:dateTime) then
xs:date(xs:dateTime($d))
else
fn:error(xs:QName("INVALID-DATE"), "Invalid date format: " || $d)
};
declare function local:print-date-array($dates) {
string-join(for $d in $dates return if ($d eq $null-date) then "NULL" else fn:string($d), ", ")
};
local:print-date-array(
local:assert-date-array-param(",2000-12-12,2012-05-03,", fn:true())
),
local:print-date-array(
local:assert-date-array-param("2000-12-12,,2012-05-03", fn:true())
)
NTN!
source to share
Several options .. in addition to the above.
-
returns a sequence of functions that, when called, returns dates
for $i in string-tokenize-to-sequence-of-strings() let $dt := my-parse-date($i) return function() { $dt ;}
or
return function() { return my-parse-date($i) ;
-
returns tokenized and verified, but unprocessed strings. Use "for" invalid, for example:
( "2014-01-22","","2017-03-30","" )
-
then there are arrays, maps, maps arrays and ... XML parseFunction () as xs: element () *:
for ... return <date>{ parse-and-validate($value) } </date>
source to share