What would be the most readable way to build a URL query string from a { 'param': 'value' }
map in XSLT/XPath 3.0?
The following function will work:
declare function local:build-uri($base-uri as xs:string, $params as map(xs:string, xs:string)) as xs:string {
if (map:size($params) ne 0) then
let $param-string := string-join(
map:keys($params)[. ne ""] ! (encode-for-uri(.) || "=" || encode-for-uri($params?(.))),
"&"
)
return $base-uri || "?" || $param-string
else
$base-uri
};
For example:
declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare variable $params := map {
"one": "1",
"two": "2",
"three": "3",
"four": "4"
};
local:build-uri("http://www.example.com", map{}),
local:build-uri("http://www.example.com", $params),
local:build-uri("", $params),
()
returns:
http://www.example.com
http://www.example.com?four=4&one=1&two=2&three=3
?four=4&one=1&two=2&three=3
Edit: To support multi-value parameters (while keeping the function body compatible with XPath), something like this should work:
declare function local:build-uri(
$base-uri as xs:string,
$params as map(xs:string, xs:string*),
$use-array-for-multivalue-params as xs:boolean (: param[]=value for PHP, etc. :)
) as xs:string {
if (map:size($params) ne 0) then
let $param-strings :=
for $param in map:keys($params)[. ne '']
return $params?($param) ! string-join((
encode-for-uri($param),
if ($use-array-for-multivalue-params and count($params?($param)) gt 1) then "[]" else "",
"=",
encode-for-uri(.)
), "")
return $base-uri || "?" || string-join($param-strings, "&")
else
$base-uri
};