skip to Main Content

I’m working with an external API in Node.js and receiving a JSON object that appears to be partially stringified. Here is a sample of what I’m getting:

{"2":["{"1":{"3":{"1":0,"3":{"1":[{"1":27,"2":{"2":"Some data is not available"}}]}},"4":"3168034526981006837"},"2":{"1":[{"1":"Brand1","2":1,"3":["2400","1900","1900","2900","2400","2900","3600","2900","2900","2900","3600","2900"],"6":"0","200":{"1":["2900","0.0","0.20833333333333334","0.0","316003","616827","0.0"]}},{"1":"Brand2","2":1,"3":["246000","165000","201000","246000","246000","301000","301000","301000","301000","246000","301000","301000"],"6":"0","200":{"1":["246000","0.22357723577235772","0.22357723577235772","0.0","","","0.22357723577235772"]}},{"1":"Brand3","2":1,"3":["6600","5400","6600","8100","8100","9900","12100","8100","8100","8100","9900","6600"],"6":"0","200":{"1":["8100","-0.18518518518518517","-0.18518518518518517","0.0","629943","2000000","-0.18518518518518517"]}}],"2":{"2":[{"3":"metric4"},{"3":"metric1"},{"3":"metric2"},{"3":"metric3"},{"3":"bid_min"},{"3":"metric5"},{"3":"metric6"}]},"3":{"1":{"1":"3"}}}}","{"1":{"3":{"1":0,"3":{"1":[{"1":27,"2":{"2":"Some data may be not available"}}]}},"4":"3168034526981006837"},"2":{"2":[{"1":"255000","2":{"1":2022,"2":5},"3":"235938"},{"1":"172300","2":{"1":2022,"2":6},"3":"159533"},{"1":"209500","2":{"1":2022,"2":7},"3":"193668"},{"1":"257000","2":{"1":2022,"2":8},"3":"236771"},{"1":"256500","2":{"1":2022,"2":9},"3":"237411"},{"1":"313800","2":{"1":2022,"2":10},"3":"291502"},{"1":"316700","2":{"1":2022,"2":11},"3":"291567"},{"1":"312000","2":{"1":2022,"2":12},"3":"289137"},{"1":"312000","2":{"1":2023,"2":1},"3":"289713"},{"1":"257000","2":{"1":2023,"2":2},"3":"237667"},{"1":"314500","2":{"1":2023,"2":3},"3":"289775"},{"1":"310500","2":{"1":2023,"2":4},"3":"287316"}],"6":"257000"}}"]}

As seen above, "1":"Brand2" is a stringified JSON object inside the main JSON. I’m having trouble working with this data, as I need to access the values of nested fields.

I’ve tried using JSON.parse() to convert the stringified part to a JavaScript object, but it’s very inconvenient to write code like this const somewhatId= JSON.parse(data[2][0])['1'][0]['2']; it works, but the overall readibility of the code is awful.

How can I properly parse this and work with it as a standard JSON object in Node.js?

2

Answers


  1. You would write a function that recursively traverses the nested object, and when it identifies strings that could be parsed as JSON, it does so, returning the corresponding object. This object could then be fed again to the same function so that even deeper encoded JSON strings will be parsed.

    This also means that string properties which can be interpreted as numbers (like "123.456") will be parsed into numbers (123.456). Same for "null", "false", "true"

    Here is an implementation and how you can run it on your example:

    function parseNestedJSON(obj) {
        if (typeof obj === "string") {
            try { // Parse this string as JSON if possible
                return parseNestedJSON(JSON.parse(obj));
            } catch {} // If not valid JSON, ignore error, and continue
        }
        if (Array.isArray(obj)) return obj.map(parseNestedJSON);
        if (Object(obj) !== obj) return obj; // Primitive
        return Object.fromEntries(Object.entries(obj).map(([k, v]) => 
            [k, parseNestedJSON(v)]
        ));
    }
    
    // Example input from the question
    const response = {"2":["{"1":{"3":{"1":0,"3":{"1":[{"1":27,"2":{"2":"Some data is not available"}}]}},"4":"3168034526981006837"},"2":{"1":[{"1":"Brand1","2":1,"3":["2400","1900","1900","2900","2400","2900","3600","2900","2900","2900","3600","2900"],"6":"0","200":{"1":["2900","0.0","0.20833333333333334","0.0","316003","616827","0.0"]}},{"1":"Brand2","2":1,"3":["246000","165000","201000","246000","246000","301000","301000","301000","301000","246000","301000","301000"],"6":"0","200":{"1":["246000","0.22357723577235772","0.22357723577235772","0.0","","","0.22357723577235772"]}},{"1":"Brand3","2":1,"3":["6600","5400","6600","8100","8100","9900","12100","8100","8100","8100","9900","6600"],"6":"0","200":{"1":["8100","-0.18518518518518517","-0.18518518518518517","0.0","629943","2000000","-0.18518518518518517"]}}],"2":{"2":[{"3":"metric4"},{"3":"metric1"},{"3":"metric2"},{"3":"metric3"},{"3":"bid_min"},{"3":"metric5"},{"3":"metric6"}]},"3":{"1":{"1":"3"}}}}","{"1":{"3":{"1":0,"3":{"1":[{"1":27,"2":{"2":"Some data may be not available"}}]}},"4":"3168034526981006837"},"2":{"2":[{"1":"255000","2":{"1":2022,"2":5},"3":"235938"},{"1":"172300","2":{"1":2022,"2":6},"3":"159533"},{"1":"209500","2":{"1":2022,"2":7},"3":"193668"},{"1":"257000","2":{"1":2022,"2":8},"3":"236771"},{"1":"256500","2":{"1":2022,"2":9},"3":"237411"},{"1":"313800","2":{"1":2022,"2":10},"3":"291502"},{"1":"316700","2":{"1":2022,"2":11},"3":"291567"},{"1":"312000","2":{"1":2022,"2":12},"3":"289137"},{"1":"312000","2":{"1":2023,"2":1},"3":"289713"},{"1":"257000","2":{"1":2023,"2":2},"3":"237667"},{"1":"314500","2":{"1":2023,"2":3},"3":"289775"},{"1":"310500","2":{"1":2023,"2":4},"3":"287316"}],"6":"257000"}}"]};
    const result = parseNestedJSON(response);
    console.log(result);
    Login or Signup to reply.
  2. You can use the reviver parameter of JSON.parse to handle JSON strings:

    JSON.parse(data, (key, value) => {
        if (typeof value == "string") {
            return JSON.parse(value);
        } else {
            return value;
        }
    });
    

    If your JSON might contain both JSON and non-JSON strings, you have options for handling that:

    JSON.parse(data, (key, value) => {
        if (typeof value == "string") {
            // Option 1: Try to parse it anyways and catch exceptions:
            try {
                return JSON.parse(value);
            } catch {
                return value;
            }
            // Option 2: Try to detect JSON strings and parse only them
            if (value[0] == "{" && value.at(-1) == "}") {
                return JSON.parse(value);
            } else {
                return value;
            }
        } else {
            return value;
        }
    });
    

    If your JSON-within-JSON might have even more JSON, use a named function for your reviver and use it recursively:

    JSON.parse(data, function parseInnerJson(key, value) {
        if (typeof value == "string") {
            return JSON.parse(value, parseInnerJson);
        } else {
            return value;
        }
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search