HiveBrain v1.2.0
Get Started
← Back to all entries
debugjavascriptMinor

Is this query-string parser bug-free?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thisbugfreeparserquerystring

Problem

I tried to implement a definitive, reliable URL query string parser that handles every corner case:

  • it tries to be efficient by avoiding regex



  • it takes full URLs or just query strings (as long as the query string begins with a question mark)



  • it ignores the hash value



  • it handles multiple equal parameter names



  • it handles parameter names that equal built-in JavaScript methods and keywords



What do you think - did I miss something?

function parseURLParams(url) {
  if (url === null) return;

  var queryStart = url.indexOf("?") + 1,
      queryEnd   = url.indexOf("#") + 1 || url.length + 1,
      query      = url.slice(queryStart, queryEnd - 1);

  if (query === url || query === "") return;

  var params = {}, 
      nvPairs = query.replace(/\+/g, " ").split("&");

  for (var i=0; i<nvPairs.length; i++) {
    var nv = nvPairs[i],
        eq = nv.indexOf("=") + 1 || nv.length + 1,
        n  = decodeURIComponent( nv.slice(0, eq - 1) ),
        v  = decodeURIComponent( nv.slice(eq) );
    if ( n !== "" ) {
      if ( !Object.prototype.hasOwnProperty.call(params, n) ) {
        params[n] = [];
      }
      params[n].push(v);
    }
  }
  return params;
}


It returns an object of arrays for parsed URLs with query strings and undefined if a query string could not be identified.

I used this in an answer over at SO.

Solution

Is it bug free? No.

These two corner-cases have been missed:

  • parameter values containing '=', i.e. 'example.com?foo==bar' (double equals) or '?foo=k=v'



  • cannot handle parameters called 'toString' and 'valueOf' (amongst others.)



The first may well count as malformed URL, but Chrome handles it and pass through == unencoded in location.search. To handle this, go back to basic indexOf usage.

The second problem's just pedantic really. You could try and work around it using !params.hasOwnProperty(n) instead of !(n in params), but you'll still get stuck if someone passes a parameter called hasOwnProperty. The only way I see around this is to fall back to some dire array-based collection populated something like:

var keys = [], params = [];
for (...) {
    var n = ..., v = ...;
    var i = keys.indexOf(n);
    if (i >= 0) {
        if (!(params[i] instanceof Array)) {
            params[i] = [params[i]];
        }
        params.push(v);
    } else {
        params[i] = v;
        keys.push(n);
    }
}


I guess you'd then have to resort to returning an array of arrays rather than an object. i.e. each element of the array returned would either be [key, value] or [key, [values]], although client might find it easier to work with if you returned something like [key, value1, value2, ...] (which caters nicely for properties without values.)

Code Snippets

var keys = [], params = [];
for (...) {
    var n = ..., v = ...;
    var i = keys.indexOf(n);
    if (i >= 0) {
        if (!(params[i] instanceof Array)) {
            params[i] = [params[i]];
        }
        params.push(v);
    } else {
        params[i] = v;
        keys.push(n);
    }
}

Context

StackExchange Code Review Q#2924, answer score: 2

Revisions (0)

No revisions yet.