I want to encode a complex json/javascript object into the standard querystring encoding.
And i want to decode this querystring back to an json/javascript object.
It should be recursively, with arrays, objects, strings, booleans and numbers.
I thought this should be easy, but was proven wrong. Does anyone have an idea, how to solve this problem?
Either in Javascript or preferably in Typescript.
>Solution :
i created a gist for to recursively encode and decode querystrings (encode = object to querystring, decode = querystring to object).
Feel free to copy/paste this in your own projects.
function encode(object) {
function reducer(obj, parentPrefix = null) {
return function (prev, key) {
const val = obj[key];
key = encodeURIComponent(key);
const prefix = parentPrefix ? `${parentPrefix}[${key}]` : key;
if (val == null || typeof val === 'function') {
prev.push(`${prefix}=`);
return prev;
}
if (typeof val === 'boolean') {
prev.push(`${prefix}=${val.toString().toUpperCase()}`);
return prev;
}
if (['number', 'string'].includes(typeof val)) {
prev.push(`${prefix}=${encodeURIComponent(val)}`);
return prev;
}
prev.push(
Object.keys(val).reduce(reducer(val, prefix), []).join('&')
);
return prev;
};
}
return Object.keys(object).reduce(reducer(object), []).join('&');
}
function decode(querystring) {
function parseValue(value) {
if (value === 'TRUE') return true;
if (value === 'FALSE') return false;
return isNaN(Number(value)) ? value : Number(value);
}
function dec(list, isArray = false): object {
let obj = isArray ? [] : {};
let recs = list.filter((item) => {
if (item.keys.length > 1) return true;
obj[item.keys[0]] = parseValue(item.value);
});
let attrs = {};
recs.map((item) => {
item.key = item.keys.shift();
attrs[item.key] = [];
return item;
}).forEach((item) => attrs[item.key].push(item));
Object.keys(attrs).forEach((attr) => {
let nextKey = attrs[attr][0].keys[0];
obj[attr] = dec(attrs[attr], typeof nextKey === 'number');
});
return obj;
}
return dec(
querystring
.split('&')
.map((item) => item.split('=').map((x) => decodeURIComponent(x)))
.map((item) => {
return {
keys: item[0]
.split(/[\[\]]/g)
.filter((n) => n)
.map((key) => (isNaN(Number(key)) ? key : Number(key))),
value: item[1],
};
})
);
}
let obj = {
name: 'John Doe',
age: 20,
dead: false,
children: [
{ name: 'Foo Doe' },
{ name: 'Bar Doe', frieds: ['Dan', 'Mike', { name: 'David' }] },
],
pc: {
setup: {
motherboard: {
features: ['FT1', 'FT2']
}
}
},
};
let encoded = encode(obj);
let decoded = decode(encoded);
console.log(decoded);