Home > Software design >  Javascript: recursively encode and decode querystring. (object to querystring, querystring to object
Javascript: recursively encode and decode querystring. (object to querystring, querystring to object

Time:01-07

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.

CodePudding user response:

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);
  •  Tags:  
  • Related