Traverse all the Nodes of a JSON Object Tree with JavaScript

Viewed 278659

I'd like to traverse a JSON object tree, but cannot find any library for that. It doesn't seem difficult but it feels like reinventing the wheel.

In XML there are so many tutorials showing how to traverse an XML tree with DOM :(

17 Answers

If you think jQuery is kind of overkill for such a primitive task, you could do something like that:

//your object
var o = { 
    foo:"bar",
    arr:[1,2,3],
    subo: {
        foo2:"bar2"
    }
};

//called with every property and its value
function process(key,value) {
    console.log(key + " : "+value);
}

function traverse(o,func) {
    for (var i in o) {
        func.apply(this,[i,o[i]]);  
        if (o[i] !== null && typeof(o[i])=="object") {
            //going one step down in the object tree!!
            traverse(o[i],func);
        }
    }
}

//that's all... no magic, no bloated framework
traverse(o,process);

A JSON object is simply a Javascript object. That's actually what JSON stands for: JavaScript Object Notation. So you'd traverse a JSON object however you'd choose to "traverse" a Javascript object in general.

In ES2017 you would do:

Object.entries(jsonObj).forEach(([key, value]) => {
    // do something with key and val
});

You can always write a function to recursively descend into the object:

function traverse(jsonObj) {
    if( jsonObj !== null && typeof jsonObj == "object" ) {
        Object.entries(jsonObj).forEach(([key, value]) => {
            // key is either an array index or object key
            traverse(value);
        });
    }
    else {
        // jsonObj is a number or string
    }
}

This should be a good starting point. I highly recommend using modern javascript methods for such things, since they make writing such code much easier.

Depends on what you want to do. Here's an example of traversing a JavaScript object tree, printing keys and values as it goes:

function js_traverse(o) {
    var type = typeof o 
    if (type == "object") {
        for (var key in o) {
            print("key: ", key)
            js_traverse(o[key])
        }
    } else {
        print(o)
    }
}

js> foobar = {foo: "bar", baz: "quux", zot: [1, 2, 3, {some: "hash"}]}
[object Object]
js> js_traverse(foobar)                 
key:  foo
bar
key:  baz
quux
key:  zot
key:  0
1
key:  1
2
key:  2
3
key:  3
key:  some
hash

Most Javascript engines do not optimize tail recursion (this might not be an issue if your JSON isn't deeply nested), but I usually err on the side of caution and do iteration instead, e.g.

function traverse(o, fn) {
    const stack = [o]

    while (stack.length) {
        const obj = stack.shift()

        Object.keys(obj).forEach((key) => {
            fn(key, obj[key], obj)
            if (obj[key] instanceof Object) {
                stack.unshift(obj[key])
                return
            }
        })
    }
}

const o = {
    name: 'Max',
    legal: false,
    other: {
        name: 'Maxwell',
        nested: {
            legal: true
        }
    }
}

const fx = (key, value, obj) => console.log(key, value)
traverse(o, fx)

var test = {
    depth00: {
        depth10: 'string'
        , depth11: 11
        , depth12: {
            depth20:'string'
            , depth21:21
        }
        , depth13: [
            {
                depth22:'2201'
                , depth23:'2301'
            }
            , {
                depth22:'2202'
                , depth23:'2302'
            }
        ]
    }
    ,depth01: {
        depth10: 'string'
        , depth11: 11
        , depth12: {
            depth20:'string'
            , depth21:21
        }
        , depth13: [
            {
                depth22:'2201'
                , depth23:'2301'
            }
            , {
                depth22:'2202'
                , depth23:'2302'
            }
        ]
    }
    , depth02: 'string'
    , dpeth03: 3
};


function traverse(result, obj, preKey) {
    if(!obj) return [];
    if (typeof obj == 'object') {
        for(var key in obj) {
            traverse(result, obj[key], (preKey || '') + (preKey ? '[' +  key + ']' : key))
        }
    } else {
        result.push({
            key: (preKey || '')
            , val: obj
        });
    }
    return result;
}

document.getElementById('textarea').value = JSON.stringify(traverse([], test), null, 2);
<textarea style="width:100%;height:600px;" id="textarea"></textarea>

We use object-scan for many data processing tasks. It's powerful once you wrap your head around it. Here is how you could do basic traversal

// const objectScan = require('object-scan');

const obj = { foo: 'bar', arr: [1, 2, 3], subo: { foo2: 'bar2' } };

objectScan(['**'], {
  reverse: false,
  filterFn: ({ key, value }) => {
    console.log(key, value);
  }
})(obj);
// => [ 'foo' ] bar
// => [ 'arr', 0 ] 1
// => [ 'arr', 1 ] 2
// => [ 'arr', 2 ] 3
// => [ 'arr' ] [ 1, 2, 3 ]
// => [ 'subo', 'foo2' ] bar2
// => [ 'subo' ] { foo2: 'bar2' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>

Disclaimer: I'm the author of object-scan

This Will read All Nodes to a map.

function readJsonFile() {
    let jsonString = getValueById("testDataContent");
    let jsonObj = JSON.parse(jsonString);
    let jsonElements = [];
    jsonElements = traverse(jsonObj, jsonElements);
    console.log(jsonElements)
}

function traverse(jsonObj, jsonElements) {
    if (jsonObj !== null && typeof jsonObj == "object") {
        Object.entries(jsonObj).forEach(([key, value]) => {
            
            if (typeof value == "object") {
                var obj = [];
                let map = new Map();
                map.set(key, traverse(value, obj))
                jsonElements.push(map);
            } else {
                var obj = [];
                obj.key = key;
                obj.value = value;
                jsonElements.push(obj);
            }
        });
    } else {

    }
    return jsonElements;
}

Related