Subject: bug#8706: 24.0.50; [PATCH] Function to build a URL query-string

From: Ian Eure <[email-masked]>
Date: Tue, 7 Jun 2011 10:07:40 -0700

Apologies for the late response, I got distracted by other things.

On May 25, 2011, at 1:31 PM, Ted Zlatanov wrote:

> On Wed, 25 May 2011 09:32:15 -0700 Ian Eure <[email-masked]> wrote: 
> 
> IE>  1. Two-element sequences should work as they do now.
> IE>  2. One-element sequences should get an empty string appended.
> IE>  3. Invalid sequences ignored.
> 
> IE> I'm not sure it makes sense to support #2, since it seems somewhat
> IE> opaque and you can do the same thing with an empty string in scenario
> IE> #1. 
> 
> Single-element parameters, shown as just "key" instead of "key=val", are
> a well-known URL query string convention.  They are not opaque.  I think
> they should be explicitly supported.
> 
As I said, they _are_ supported. You just have to explicitly pass an empty string in the pair:

(url-build-query-string '(("a" "b") ("c" "")) -> "a=b&c="

I believe this is the correct behavior. It's also precisely what url-parse-query-string returns:

(url-parse-query-string "a=b&c=") -> (("c" "") ("a" "b"))

This is also relevant to Stefan's comment. All else being equal, I think we should support the same conventions it does. The one issue I discovered is how it handles sending multiple values for the same key. Given "a=one&a=two", it returns: (("a" "one" "two")), which my previous iterations don't know what to do with.

Here's an updated patch which should correctly support everything url-parse-query-string produces. I also updated it to allow empty keys, as this was fairly easy with the code refactored to support (key val val val) syntax.

Updated patch:

--- url-util.el.orig	2011-06-07 09:56:36.000000000 -0700
+++ url-util.el	2011-06-07 10:06:09.000000000 -0700
@@ -281,6 +281,31 @@
 	  (setq retval (cons (list key val) retval)))))
     retval))
 
+;;;###autoload
+(defun url-build-query-string (query)
+  "Build a query-string.
+
+Given a QUERY in the form:
+'((key1 val1)
+  (key2 val2)
+  (key3 val1 val2))
+
+\(This is the same format as produced by `url-parse-query-string')
+
+This will return a string
+`key1=val1&key2=val2&key3=val1&key3=val2'. Keys may be strings or
+symbols; if they are symbols, the string name will be used."
+
+  (mapconcat
+   (lambda (key-vals)
+     (let ((escaped
+            (mapcar (lambda (sym)
+                      (url-hexify-string (format "%s" sym))) key-vals)))
+
+       (mapconcat (lambda (val) (format "%s=%s" (car escaped) val))
+                  (or (cdr escaped) '("")) "&")))
+   query "&"))
+
 (defun url-unhex (x)
   (if (> x ?9)
       (if (>= x ?a)