JavaScript / jQuery中的$ .param()反函数
鉴于以下forms:
<form> <input name="foo" value="bar"> <input name="hello" value="hello world"> </form>
我可以使用$.param( .. )
结构来序列化表单:
$.param( $('form input') ) => foo=bar&hello=hello+world
我怎样才能反序列化JavaScript的上述string,并得到一个哈希回来?
例如,
$.magicFunction("foo=bar&hello=hello+world") => {'foo' : 'bar', 'hello' : 'hello world'}
参考: jQuery.param( obj )
。
你应该使用jQuery BBQ的deparam函数。 这是经过充分testing和logging。
这是我前一段时间写的一个稍微修改过的版本,用来做类似的事情。
var QueryStringToHash = function QueryStringToHash (query) { var query_string = {}; var vars = query.split("&"); for (var i=0;i<vars.length;i++) { var pair = vars[i].split("="); pair[0] = decodeURIComponent(pair[0]); pair[1] = decodeURIComponent(pair[1]); // If first entry with this name if (typeof query_string[pair[0]] === "undefined") { query_string[pair[0]] = pair[1]; // If second entry with this name } else if (typeof query_string[pair[0]] === "string") { var arr = [ query_string[pair[0]], pair[1] ]; query_string[pair[0]] = arr; // If third or later entry with this name } else { query_string[pair[0]].push(pair[1]); } } return query_string; };
这个简短的function性方法呢?
function parseParams(str) { return str.split('&').reduce(function (params, param) { var paramSplit = param.split('=').map(function (value) { return decodeURIComponent(value.replace('+', ' ')); }); params[paramSplit[0]] = paramSplit[1]; return params; }, {}); }
例:
parseParams("this=is&just=an&example") // Object {this: "is", just: "an", example: undefined}
我的答案:
function(query){ var setValue = function(root, path, value){ if(path.length > 1){ var dir = path.shift(); if( typeof root[dir] == 'undefined' ){ root[dir] = path[0] == '' ? [] : {}; } arguments.callee(root[dir], path, value); }else{ if( root instanceof Array ){ root.push(value); }else{ root[path] = value; } } }; var nvp = query.split('&'); var data = {}; for( var i = 0 ; i < nvp.length ; i++ ){ var pair = nvp[i].split('='); var name = decodeURIComponent(pair[0]); var value = decodeURIComponent(pair[1]); var path = name.match(/(^[^\[]+)(\[.*\]$)?/); var first = path[1]; if(path[2]){ //case of 'array[level1]' || 'array[level1][level2]' path = path[2].match(/(?=\[(.*)\]$)/)[1].split('][') }else{ //case of 'name' path = []; } path.unshift(first); setValue(data, path, value); } return data; }
我正在使用David Dorward的答案,并且意识到它不像PHP或Ruby on Rails那样performance如何parsing参数:
1)一个variables只有一个数组,如果以[]
结尾,比如?choice[]=1&choice[]=12
,而不是当它是?a=1&a=2
2)当多个参数存在同名时,后面的replace早先的那些,就像在PHP服务器上(Ruby on Rails保留第一个而忽略后面的那些)一样,例如?a=1&b=2&a=3
所以修改大卫的版本,我有:
function QueryStringToHash(query) { if (query == '') return null; var hash = {}; var vars = query.split("&"); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split("="); var k = decodeURIComponent(pair[0]); var v = decodeURIComponent(pair[1]); // If it is the first entry with this name if (typeof hash[k] === "undefined") { if (k.substr(k.length-2) != '[]') // not end with []. cannot use negative index as IE doesn't understand it hash[k] = v; else hash[k.substr(0, k.length-2)] = [v]; // If subsequent entry with this name and not array } else if (typeof hash[k] === "string") { hash[k] = v; // replace it // If subsequent entry with this name and is array } else { hash[k.substr(0, k.length-2)].push(v); } } return hash; };
这是testing相当彻底。
以下是如何创build一个新的jQuery函数:
jQuery.unparam = function (value) { var // Object that holds names => values. params = {}, // Get query string pieces (separated by &) pieces = value.split('&'), // Temporary variables used in loop. pair, i, l; // Loop through query string pieces and assign params. for (i = 0, l = pieces.length; i < l; i++) { pair = pieces[i].split('=', 2); // Repeated parameters with the same name are overwritten. Parameters // with no value get set to boolean true. params[decodeURIComponent(pair[0])] = (pair.length == 2 ? decodeURIComponent(pair[1].replace(/\+/g, ' ')) : true); } return params; };
感谢他http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
很容易:D
function params_unserialize(p){ var ret = {}, seg = p.replace(/^\?/,'').split('&'), len = seg.length, i = 0, s; for (;i<len;i++) { if (!seg[i]) { continue; } s = seg[i].split('='); ret[s[0]] = s[1]; } return ret;}
我知道这是一个古老的线程,但也许还有一些相关的呢?
受Jacky Li良好的解决scheme的启发,我尝试了一下我自己的一个小小的变化,以便能够把数组和对象的任意组合作为input。 我研究了PHP如何做到这一点,并试图获得“相似”的东西。 这是我的代码:
function getargs(str){ var ret={}; function build(urlnam,urlval,obj){ // extend the return object ... var i,k,o=obj, x, rx=/\[([^\]]*)\]/g, idx=[urlnam.replace(rx,'')]; while (x=rx.exec(urlnam)) idx.push(x[1]); while(true){ k=idx.shift(); if(k.trim()=='') {// key is empty: autoincremented index if (o.constructor.name=='Array') k=o.length; // for Array else if (o===obj ) {k=null} // for first level property name else {k=-1; // for Object for(i in o) if (+i>k) k=+i; k++; } } if(idx.length) { // set up an array if the next key (idx[0]) appears to be // numeric or empty, otherwise set up an object: if (o[k]==null || typeof o[k]!='object') o[k]=isNaN(idx[0])?{}:[]; o=o[k]; // move on to the next level } else { // OK, time to store the urlval in its chosen place ... // console.log('key',k,'val',urlval); o[k]=urlval===""?null:urlval; break; // ... and leave the while loop. } } return obj; } // ncnvt: is a flag that governs the conversion of // numeric strings into numbers var ncnvt=true,i,k,p,v,argarr=[], ar=(str||window.location.search.substring(1)).split("&"), l=ar.length; for (i=0;i<l;i++) {if (ar[i]==="") continue; p=ar[i].split("=");k=decodeURIComponent(p[0]); v=p[1];v=(v!=null)?decodeURIComponent(v.replace(/\+/g,'%20')):''; if (ncnvt && v.trim()>"" && !isNaN(v)) v-=0; argarr.push([k,v]); // array: key-value-pairs of all arguments } for (i=0,l=argarr.length;i<l;i++) build(argarr[i][0],argarr[i][1],ret); return ret; }
如果函数调用时没有str
参数,它将假设window.location.search.slice(1)
作为input。
一些例子:
['a=1&a=2', // 1 'x[y][0][z][]=1', // 2 'hello=[%22world%22]&world=hello', // 3 'a=1&a=2&&b&c=3&d=&=e&', // 4 'fld[2][]=2&fld[][]=3&fld[3][]=4&fld[]=bb&fld[]=cc', // 5 $.param({a:[[1,2],[3,4],{aa:'one',bb:'two'},[5,6]]}), // 6 'a[]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13',// 7 'a[x]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13'// 8 ].map(function(v){return JSON.stringify(getargs(v));}).join('\n')
结果是
{"a":2} // 1 {"x":{"y":[{"z":[1]}]}} // 2 {"hello":"[\"world\"]","world":"hello"} // 3 {"a":2,"b":null,"c":3,"d":null,"null":"e"} // 4 = { a: 2, b: null, c: 3, d: null, null: "e" } {"fld":[null,null,[2],[3,4],"bb","cc"]} // 5 {"a":[[1,2],[3,4],{"aa":"one","bb":"two"},[5,6]]} // 6 {"a":["hi",2,null,[7,99],13]} // 7 {"a":{"0":2,"3":[7,99],"4":13,"x":"hi"}} // 8
Jacky Li的解决scheme将生产a
作为普通物品的外部容器
{a:{"0":["1","2"],"1":["3","4"],"2":["5","6"]}} // 6: JackyLi's output
getargs()
查看任何级别的第一个给定索引,以确定这个级别是一个对象(非数字索引)还是一个数组(数字或空),从而产生如列表中所示的输出。 6)。
如果当前对象是一个数组,那么null
被插入到需要表示空位置的地方。 数组总是连续编号,以0为基础)。
注意,在例子中没有。 8尽pipe我们现在正在处理一个对象而不是一个数组,但是空索引的“自动增量”仍然有效。
就我testing过的,我的getargs()
与Chriss Roger在接受的答案中提到的很棒的jQuery $.deparam()
插件的行为非常相似。 主要区别在于getargs
运行时没有使用jQuery,而且在$.deparam()
不会自动增加对象:
JSON.stringify($.deparam('a[x]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13').a);
结果是
{"3":["7","99"],"x":"hi","undefined":"13"}
在$.deparam()
,索引[]
被解释为undefined
而不是自动增量数字索引。
以下是我在服务器端JScript ASP Classic页面( 演示 )中使用的JavaScript实现:
// Transforms a query string in the form x[y][0][z][]=1 into {x:{y:[{z:[1]}]}} function parseJQueryParams(p) { var params = {}; var pairs = p.split('&'); for (var i=0; i<pairs.length; i++) { var pair = pairs[i].split('='); var indices = []; var name = decodeURIComponent(pair[0]), value = decodeURIComponent(pair[1]); var name = name.replace(/\[([^\]]*)\]/g, function(k, idx) { indices.push(idx); return ""; }); indices.unshift(name); var o = params; for (var j=0; j<indices.length-1; j++) { var idx = indices[j]; var nextIdx = indices[j+1]; if (!o[idx]) { if ((nextIdx == "") || (/^[0-9]+$/.test(nextIdx))) o[idx] = []; else o[idx] = {}; } o = o[idx]; } idx = indices[indices.length-1]; if (idx == "") { o.push(value); } else { o[idx] = value; } } return params; }
这是我在Coffeescript的版本。 也适用于像http://localhost:4567/index.html?hello=[%22world%22]&world=hello#/home
getQueryString: (url)-> return null if typeof url isnt 'string' or url.indexOf("http") is -1 split = url.split "?" return null if split.length < 2 path = split[1] hash_pos = path.indexOf "#" path = path[0...hash_pos] if hash_pos isnt -1 data = path.split "&" ret = {} for d in data [name, val] = d.split "=" name = decodeURIComponent name val = decodeURIComponent val try ret[name] = JSON.parse val catch error ret[name] = val return ret
我想出了这个解决scheme,其行为像.Net函数HttpUtility.ParseQueryString
。
在结果中,查询string参数以值列表的forms存储在属性中,因此qsObj["param"]
将与在.Net中调用GetValues("param")
相同。
我希望你喜欢它。 JQuery不是必需的。
var parseQueryString = function (querystring) { var qsObj = new Object(); if (querystring) { var parts = querystring.replace(/\?/, "").split("&"); var up = function (k, v) { var a = qsObj[k]; if (typeof a == "undefined") { qsObj[k] = [v]; } else if (a instanceof Array) { a.push(v); } }; for (var i in parts) { var part = parts[i]; var kv = part.split('='); if (kv.length == 1) { var v = decodeURIComponent(kv[0] || ""); up(null, v); } else if (kv.length > 1) { var k = decodeURIComponent(kv[0] || ""); var v = decodeURIComponent(kv[1] || ""); up(k, v); } } } return qsObj; };
以下是如何使用它:
var qsObj = parseQueryString("a=1&a=2&&b&c=3&d=&=e&");
要在控制台正确types中预览结果,请执行以下操作:
JSON.stringify(qsObj)
输出:
"{"a":["1","2"],"null":["","b",""],"c":["3"],"d":[""],"":["e"]}"
在CSS-Tricks ( Nicholas Ortenzio的原始资料)中有一个漂亮的单引号 :
function getQueryParameters(str) { return (str || document.location.search).replace(/(^\?)/,'').split("&").map(function(n){return n = n.split("="),this[n[0]] = n[1],this}.bind({}))[0]; }
真正聪明的部分是它如何使用匿名函数的this
对象,为string中的每个查询添加一个键/值对。 这就是说,还有一些改进的余地。 我已经修改了一下,下面的变化:
-
增加了对空string和非stringinput的处理。
-
处理了URI编码的string(
%40
– >@
等)。 -
当input为空时,删除了
document.location.search
的默认使用。 -
更改了名称,使其更具可读性,添加了评论。
function deparam(str) { // Uses an empty 'this' to build up the results internally function splitQuery(query) { query = query.split('=').map(decodeURIComponent); this[query[0]] = query[1]; return this; } // Catch bad input if (!str || !(typeof str === 'string' || str instanceof String)) return {}; // Split the string, run splitQuery on each piece, and return 'this' var queries = str.replace(/(^\?)/,'').split('&'); return queries.map(splitQuery.bind({}))[0]; }
答案可以使用一点jQuery的优雅:
(function($) { var re = /([^&=]+)=?([^&]*)/g; var decodeRE = /\+/g; // Regex for replacing addition symbol with a space var decode = function (str) {return decodeURIComponent( str.replace(decodeRE, " ") );}; $.parseParams = function(query) { var params = {}, e; while ( e = re.exec(query) ) { var k = decode( e[1] ), v = decode( e[2] ); if (k.substring(k.length - 2) === '[]') { k = k.substring(0, k.length - 2); (params[k] || (params[k] = [])).push(v); } else params[k] = v; } return params; }; })(jQuery);
你可以使用jQuery本身的函数.serializeArray()
( Link )。 这个函数返回一个键值对的数组。 结果示例:
[ { name: "id", value: "1" }, { name: "version", value: "100" } ]