let's see - substring to get ? and strip any trailing #... fragment identifier
then tokenize on [&;] (actually [;&] to get name=value pairs, which are separated by & or (less commonly) ;
then substring-before and after, or tokenize again, to get before and after the = (name value)
then uridecode the name and the value separately
let $query := substring-after($uri, '?'),
$beforefrag := substring-before($query || '#', '#')
return
tokenize($beforefrag, '[;&]')
! [substring-before(., '='), substring-after(., '=') ]
! map:entry(local:uridecode(.(1), local:uridecode(.(2))
might give us a sequene of map entries, and we can use map:merge on that.
If we know our input is plausibly encoded, we could use
declare function local:uridecode($input as xs:string?) as xs:string?
{
parse-xml-fragment(replace($input, '=(..)', '&x$1;'))
};
but a better version would just replace the two hex characters. It's really unfortunate we don't have a version of replace() that takes a function argument to be called for each matching subexpression, ala perl's e flag.```
and of course you can put that into
(...) => map:merge()