Since I did not find anything sensible, I solved the problem for myself with the following function:
function getUrlPairs(obj) { var getType = obj => obj === undefined ? 'undefined' : (obj === null ? 'null' : obj.constructor.name), simple = (path, val) => path + '=' + val, simpleEnc = (path, val) => simple(path, encodeURIComponent(val)), typeFunc = { null: simple, undefined: (path, val) => simple(path, ''), Number: simple, Boolean: simple, String: simpleEnc, RegExp: simpleEnc, Date: (path, val) => simpleEnc(path, val.toJSON()), Object: (path, val, dot = '.') => iterate(val, path + dot, name => name), Array: (path, val) => iterate(val, path, name => '[' + name + ']') }, pairs = [], type = getType(obj); function iterate(obj, prefix, wrap) { for (const[name, val] of Object.entries(obj)) { const call = typeFunc[getType(val)]; const tmp = call && call(prefix + wrap(name), val); call && tmp && pairs.push(tmp); } } typeFunc[type] && typeFunc[type]('', obj, ''); return pairs; }
Below is an example of using and testing the object serialization:
var obj = { dirty: "&%[]?", nullable: null, undef: undefined, numberProp: 123, now: new Date(), boolProp: true, obj: { objProp1: "objStr", objProp2: false, nullable: null, objInObj: { aa:"aa", bb: true } }, arr: [{ regexProp: new RegExp('^[a-z0-9]+$', 'i'), arrProp12: 321, arrProp13: true, arrProp14: { a: "absdefg", b: true } }, { arrProp21: "rts", arrProp22: 987, arrProp23: false }, ["arrInArr1", "arrInArr2"], [{test:"str"}], null, new Date() ] }; function getUrlPairs(obj) { var getType = obj => obj === undefined ? 'undefined' : (obj === null ? 'null' : obj.constructor.name), simple = (path, val) => path + '=' + val, simpleEnc = (path, val) => simple(path, encodeURIComponent(val)), typeFunc = { null: simple, undefined: (path, val) => simple(path, ''), Number: simple, Boolean: simple, String: simpleEnc, RegExp: simpleEnc, Date: (path, val) => simpleEnc(path, val.toJSON()), Object: (path, val, dot = '.') => iterate(val, path + dot, name => name), Array: (path, val) => iterate(val, path, name => '[' + name + ']') }, pairs = [], type = getType(obj); function iterate(obj, prefix, wrap) { for (const[name, val] of Object.entries(obj)) { const call = typeFunc[getType(val)]; const tmp = call && call(prefix + wrap(name), val); call && tmp && pairs.push(tmp); } } typeFunc[type] && typeFunc[type]('', obj, ''); return pairs; } var res = getUrlPairs(obj); document.getElementById("output").innerHTML = res.join("\n") + "\n\n" + res.join("&");
textarea { width: 90%; }
<textarea rows="20" id="output"></textarea>