Allow callers to disable PHP/Rails style parameter mungeing in querystring.stringify
authorThomas Lee <tom@tom-debian.sensis.com.au>
Wed, 28 Apr 2010 06:31:28 +0000 (16:31 +1000)
committerRyan Dahl <ry@tinyclouds.org>
Wed, 28 Apr 2010 23:18:48 +0000 (16:18 -0700)
doc/api.markdown
lib/querystring.js
test/simple/test-querystring.js

index 041ec39..6042805 100644 (file)
@@ -2459,7 +2459,7 @@ Take a base URL, and a href URL, and resolve them as a browser would for an anch
 
 This module provides utilities for dealing with query strings.  It provides the following methods:
 
-### querystring.stringify(obj, sep='&', eq='=')
+### querystring.stringify(obj, sep='&', eq='=', munge=true)
 
 Serialize an object to a query string.  Optionally override the default separator and assignment characters.
 Example:
@@ -2468,6 +2468,32 @@ Example:
     // returns
     'foo=bar'
 
+    querystring.stringify({foo: 'bar', baz: 'bob'}, ';', ':')
+    // returns
+    'foo:bar;baz:bob'
+
+By default, this function will perform PHP/Rails-style parameter mungeing for arrays and objects used as
+values within `obj`.
+Example:
+
+    querystring.stringify({foo: 'bar', foo: 'baz', foo: 'boz'})
+    // returns
+    'foo[]=bar&foo[]=baz&foo[]=boz'
+
+    querystring.stringify({foo: {bar: 'baz'}})
+    // returns
+    'foo[bar]=baz'
+
+If you wish to disable the array mungeing (e.g. when generating parameters for a Java servlet), you
+can set the `munge` argument to `false`.
+Example:
+
+    querystring.stringify({foo: 'bar', foo: 'baz', foo: 'boz'}, '&', '=', false)
+    // returns
+    'foo=bar&foo=baz&foo=boz'
+
+Note that when `munge` is `false`, parameter names with object values will still be munged.
+
 ### querystring.parse(str, sep='&', eq='=')
 
 Deserialize a query string to an object.  Optionally override the default separator and assignment characters.
@@ -2478,6 +2504,8 @@ Deserialize a query string to an object.  Optionally override the default separa
     , 'b': 'c'
     }
 
+This function can parse both munged and unmunged query strings (see `stringify` for details).
+
 ### querystring.escape
 
 The escape function used by `querystring.stringify`, provided so that it could be overridden if necessary.
index 83e0a0f..df19123 100644 (file)
@@ -21,10 +21,12 @@ var stack = [];
  * @param obj {Variant} any arbitrary value to convert to query string
  * @param sep {String} (optional) Character that should join param k=v pairs together. Default: "&"
  * @param eq  {String} (optional) Character that should join keys to their values. Default: "="
+ * @param munge {Boolean} (optional) Indicate whether array/object params should be munged, PHP/Rails-style. Default: true
  * @param name {String} (optional) Name of the current key, for handling children recursively.
  * @static
  */
-QueryString.stringify = function (obj, sep, eq, name) {
+QueryString.stringify = function (obj, sep, eq, munge, name) {
+  munge = typeof(munge) == "undefined" ? true : munge;
   sep = sep || "&";
   eq = eq || "=";
   if (isA(obj, null) || isA(obj, undefined) || typeof(obj) === 'function') {
@@ -37,9 +39,9 @@ QueryString.stringify = function (obj, sep, eq, name) {
   }
   if (isA(obj, [])) {
     var s = [];
-    name = name+'[]';
+    name = name+(munge ? '[]' : '');
     for (var i = 0, l = obj.length; i < l; i ++) {
-      s.push( QueryString.stringify(obj[i], sep, eq, name) );
+      s.push( QueryString.stringify(obj[i], sep, eq, munge, name) );
     }
     return s.join(sep);
   }
@@ -59,7 +61,7 @@ QueryString.stringify = function (obj, sep, eq, name) {
   for (var i = 0, l = keys.length; i < l; i++) {
     var key = keys[i];
     var n = begin + key + end;
-    s.push(QueryString.stringify(obj[key], sep, eq, n));
+    s.push(QueryString.stringify(obj[key], sep, eq, munge, n));
   }
 
   stack.pop();
index 3e018a6..b328b4e 100644 (file)
@@ -58,6 +58,16 @@ var qsWeirdObjects = [
 ];
 }
 
+var qsNoMungeTestCases = [
+  ["", {}],
+  ["foo=bar&foo=baz", {"foo": ["bar", "baz"]}],
+  ["blah=burp", {"blah": "burp"}],
+  ["gragh=1&gragh=3&goo=2", {"gragh": ["1", "3"], "goo": "2"}],
+  ["frappucino=muffin&goat%5B%5D=scone&pond=moose",
+   {"frappucino": "muffin", "goat[]": "scone", "pond": "moose"}],
+  ["obj%5Btrololol%5D=yes&obj%5Blololo%5D=no", {"obj": {"trololol": "yes", "lololo": "no"}}],
+];
+
 // test that the canonical qs is parsed properly.
 qsTestCases.forEach(function (testCase) {
   assert.deepEqual(testCase[2], qs.parse(testCase[0]));
@@ -73,6 +83,10 @@ qsWeirdObjects.forEach(function (testCase) {
   assert.deepEqual(testCase[2], qs.parse(testCase[1]));
 });
 
+qsNoMungeTestCases.forEach(function (testCase) {
+  assert.deepEqual(testCase[0], qs.stringify(testCase[1], "&", "=", false));
+});
+
 // test the nested qs-in-qs case
 var f = qs.parse("a=b&q=x%3Dy%26y%3Dz");
 f.q = qs.parse(f.q);