Search code examples
javascriptinternet-explorer-11ecmascript-5

Access deep object member of embeded JSON


The following function does an amazing job of retrieving a deep object key value with a dot notated string:

function getPathValue(obj, path) {
  return new Function('_', 'return _.' + path)(obj);
}

For example, it returns the value for a key like "bar" using the following as the path argument:

'data.abc.foo.bar'

However, some API's pack additional stringified JSON into some key values.

I'm looking for an enhancement to the above function that will handle that case.

For example, Stack's own WebSocket service:

wss://qa.sockets.stackexchange.com/

returns strings like this:

{"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"stackoverflow.com\",\"id\":53819390,\"titleEncodedFancy\":\"Should I start with webGL1 or webGL2 when using Three.js\",\"bodySummary\":\"Spent some time learning and understanding the basics of webGL and I'm now diving into Three.js.\\n\\nI noticed that I have the option of using webGL1 or webGL2. Seems as if webGL1 is the default, however ...\",\"tags\":[\"three.js\",\"webgl\"],\"lastActivityDate\":1545064508,\"url\":\"https://stackoverflow.com/questions/53819390/should-i-start-with-webgl1-or-webgl2-when-using-three-js\",\"ownerUrl\":\"https://stackoverflow.com/users/8226111/romanrogers\",\"ownerDisplayName\":\"romanrogers\",\"apiSiteParameter\":\"stackoverflow\"}"}

And I'd like to be able to use the above function to retrieve values with an input string like:

'data.bodySummary'

Perhaps the input string could look something like:

'data/bodySummary'

where the solidus (/slash) represents a JSON.parse().

Note, this needs to be dynamic as I'd like to make it where the end user can select arbitrary keys to return values for in the general case.


Solution

  • You could check if the item from where you take a property is a string an perform a parsing. The return the value of the property.

    function getValue(object, path) {
        return path
            .split('.')
            .reduce((o, k) => (typeof o === 'string' ? JSON.parse(o) : o)[k], object);
    }
    
    var data = {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"stackoverflow.com\",\"id\":53819390,\"titleEncodedFancy\":\"Should I start with webGL1 or webGL2 when using Three.js\",\"bodySummary\":\"Spent some time learning and understanding the basics of webGL and I'm now diving into Three.js.\\n\\nI noticed that I have the option of using webGL1 or webGL2. Seems as if webGL1 is the default, however ...\",\"tags\":[\"three.js\",\"webgl\"],\"lastActivityDate\":1545064508,\"url\":\"https://stackoverflow.com/questions/53819390/should-i-start-with-webgl1-or-webgl2-when-using-three-js\",\"ownerUrl\":\"https://stackoverflow.com/users/8226111/romanrogers\",\"ownerDisplayName\":\"romanrogers\",\"apiSiteParameter\":\"stackoverflow\"}"};
    
    console.log(getValue(data, 'data.bodySummary'));

    ES5

    function getValue(object, path) {
        return path
            .split('.')
            .reduce(function (o, k) {
                return (typeof o === 'string' ? JSON.parse(o) : o)[k];
            }, object);
    }
    
    var data = {"action":"155-questions-active","data":"{\"siteBaseHostAddress\":\"stackoverflow.com\",\"id\":53819390,\"titleEncodedFancy\":\"Should I start with webGL1 or webGL2 when using Three.js\",\"bodySummary\":\"Spent some time learning and understanding the basics of webGL and I'm now diving into Three.js.\\n\\nI noticed that I have the option of using webGL1 or webGL2. Seems as if webGL1 is the default, however ...\",\"tags\":[\"three.js\",\"webgl\"],\"lastActivityDate\":1545064508,\"url\":\"https://stackoverflow.com/questions/53819390/should-i-start-with-webgl1-or-webgl2-when-using-three-js\",\"ownerUrl\":\"https://stackoverflow.com/users/8226111/romanrogers\",\"ownerDisplayName\":\"romanrogers\",\"apiSiteParameter\":\"stackoverflow\"}"};
    
    console.log(getValue(data, 'data.bodySummary'));