Search code examples
javascriptjsonobject

Parsing nested stringified json objects


I have some legacy code that returns nested stringified json as such:

"{ "value1": "name", "value2": "lastName", "value3": "{ \"subValue1\": \"\", \"subValue2\": null }" }"

and I am trying to write a parser function that would go into those nested objects, and parse them as well, however, I get a strange side effect where the parser does its job, but also adds extra values. Using example above it basically does this:

{
  0: "N",
  1: "O",
  value1: "name",
  value2: "NO",
  value3: {
    subValue1: "",
    subValue2: null,
  }
}

and I can't seem to debug it :/

Here is the parser in question:

const addValuesToObject = ({ object, keysArray, value }) => {
    for (let i = 0; i < keysArray.length; i++) {
       const lastKey = i === keysArray.length - 1;
       object = object[keysArray[i]] = object[keysArray[i]] || (lastKey ? value : {});
    }
    
    return object;
 };

 const isStringifiedObject = (value) => typeof value === "string" && value[0] === "{" && value[value.length - 1] === "}";

 const nestedJsonParser = (stringifiedObject) => {
        
    if (!stringifiedObject) return {};

    let newObject = {};

    const parser = (value, objectPath = []) => {
      const isStringified = isStringifiedObject(value);

      // if it's a stringified object, parse and add values;
      if (isStringified) {
        const newParsedObject = JSON.parse(value);
        const keys = Object.keys(newParsedObject);

        keys.forEach((key) => {
          parser(newParsedObject[key], [...objectPath, key]);
        });
      } else {
        const newEntry = addValuesToObject({ value, keysArray: objectPath, object: newObject });
        newObject = { ...newObject, ...newEntry };
      }
    };

    parser(stringifiedObject);

    return newObject;
  };

if anyone can help me to avoid the whole adding of "0": "Y" thing, it would be greatly appreciated.

Edit: I'm fine with isStringifiedObject being a little wonky as it is, I don't expect to get usable strings that start/end with {}


Solution

  • You can use JSON.parse with a "reviver" that recursively tries to parse every decoded string as json (and returns the argument as is if that fails):

    function parseNested(str) {
        try {
            return JSON.parse(str, (_, val) => {
                if (typeof val === 'string')
                    return parseNested(val)
                return val
            })
        } catch (exc) {
            return str
        }
    }
    
    // demo
    
    obj = {
        'abc': 'def',
        'foo': 'bar',
        'nest1': JSON.stringify({deep1: 123}),
        'nest2': JSON.stringify({deep2: JSON.stringify({deep3: ['something', 'else']})}),
    }
    
    weird = JSON.stringify(obj)
    console.log(weird)
    console.log(parseNested(weird))