xpathxslt-3.0xpath-3.0

Building a URL query string from a map of parameters with XPath


What would be the most readable way to build a URL query string from a { 'param': 'value' } map in XSLT/XPath 3.0?


Solution

  • 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
    };