Traverse all the Nodes of a JSON Object Tree with

2018-12-31 15:43发布

问题:

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 :(

回答1:

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);


回答2:

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.



回答3:

function traverse(o ) {
    for (i in o) {
        if (!!o[i] && typeof(o[i])==\"object\") {
            console.log(i, o[i])
            traverse(o[i] );
        }
    }
}


回答4:

There\'s a new library for traversing JSON data with JavaScript that supports many different use cases.

https://npmjs.org/package/traverse

https://github.com/substack/js-traverse

It works with all kinds of JavaScript objects. It even detects cycles.

It provides the path of each node, too.



回答5:

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


回答6:

If you\'re traversing an actual JSON string then you can use a reviver function.

function traverse (json, callback) {
  JSON.parse(json, function (key, value) {
    if (key !== \'\') {
      callback.call(this, key, value)
    }
    return value
  })
}

traverse(\'{\"a\":{\"b\":{\"c\":{\"d\":1}},\"e\":{\"f\":2}}}\', function (key, value) {
  console.log(arguments)
})

When traversing an object:

function traverse (obj, callback, trail) {
  trail = trail || []

  Object.keys(obj).forEach(function (key) {
    var value = obj[key]

    if (Object.getPrototypeOf(value) === Object.prototype) {
      traverse(value, callback, trail.concat(key))
    } else {
      callback.call(obj, key, value, trail)
    }
  })
}

traverse({a: {b: {c: {d: 1}}, e: {f: 2}}}, function (key, value, trail) {
  console.log(arguments)
})


回答7:

I wanted to use the perfect solution of @TheHippo in an anonymous function, without use of process and trigger functions. The following worked for me, sharing for novice programmers like myself.

(function traverse(o) {
    for (var i in o) {
        console.log(\'key : \' + i + \', value: \' + o[i]);

        if (o[i] !== null && typeof(o[i])==\"object\") {
            //going on step down in the object tree!!
            traverse(o[i]);
        }
    }
  })
  (json);


回答8:

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)


回答9:

For a newer way to do it if you don\'t mind dropping IE and mainly supporting more current browsers (check kangax\'s es6 table for compatibility). You can use es2015 generators for this. I\'ve updated @TheHippo\'s answer accordingly. Of course if you really want IE support you can use the babel JavaScript transpiler.

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

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

//that\'s all... no magic, no bloated framework
for(var [key, value] of traverse(o)) {
  console.log(key, value);
}

If you want only own enumerable properties (basically non-prototype chain properties) you can change it to iterate using Object.keys and a for...of loop instead:

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

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

//that\'s all... no magic, no bloated framework
for(var [key, value] of traverse(o)) {
  console.log(key, value);
}



回答10:

My Script:

op_needed = [];
callback_func = function(val) {
  var i, j, len;
  results = [];
  for (j = 0, len = val.length; j < len; j++) {
    i = val[j];
    if (i[\'children\'].length !== 0) {
      call_func(i[\'children\']);
    } else {
      op_needed.push(i[\'rel_path\']);
    }
  }
  return op_needed;
};

Input JSON:

[
    {
        \"id\": null, 
        \"name\": \"output\",   
        \"asset_type_assoc\": [], 
        \"rel_path\": \"output\",
        \"children\": [
            {
                \"id\": null, 
                \"name\": \"output\",   
                \"asset_type_assoc\": [], 
                \"rel_path\": \"output/f1\",
                \"children\": [
                    {
                        \"id\": null, 
                        \"name\": \"v#\",
                        \"asset_type_assoc\": [], 
                        \"rel_path\": \"output/f1/ver\",
                        \"children\": []
                    }
                ]
            }
       ]
   }
]

Function Call:

callback_func(inp_json);

Output as per my Need:

[\"output/f1/ver\"]


回答11:

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>



回答12:

The best solution for me was the following:

simple and without using any framework

    var doSomethingForAll = function (arg) {
       if (arg != undefined && arg.length > 0) {
            arg.map(function (item) {
                  // do something for item
                  doSomethingForAll (item.subitem)
             });
        }
     }


回答13:

You can get all keys / values and preserve the hierarchy with this

// get keys of an object or array
function getkeys(z){
  var out=[]; 
  for(var i in z){out.push(i)};
  return out;
}

// print all inside an object
function allInternalObjs(data, name) {
  name = name || \'data\';
  return getkeys(data).reduce(function(olist, k){
    var v = data[k];
    if(typeof v === \'object\') { olist.push.apply(olist, allInternalObjs(v, name + \'.\' + k)); }
    else { olist.push(name + \'.\' + k + \' = \' + v); }
    return olist;
  }, []);
}

// run with this
allInternalObjs({\'a\':[{\'b\':\'c\'},{\'d\':{\'e\':5}}],\'f\':{\'g\':\'h\'}}, \'ob\')

This is a modification on (https://stackoverflow.com/a/25063574/1484447)



回答14:

             var localdata = [{\'\'}]// Your json array
              for (var j = 0; j < localdata.length; j++) 
               {$(localdata).each(function(index,item)
                {
                 $(\'#tbl\').append(\'<tr><td>\' + item.FirstName +\'</td></tr>);
                 }


回答15:

I\'ve created library to traverse and edit deep nested JS objects. Check out API here: https://github.com/dominik791

You can also play with the library interactively using demo app: https://dominik791.github.io/obj-traverse-demo/

Examples of usage: You should always have root object which is the first parameter of each method:

var rootObj = {
  name: \'rootObject\',
  children: [
    {
      \'name\': \'child1\',
       children: [ ... ]
    },
    {
       \'name\': \'child2\',
       children: [ ... ]
    }
  ]
};

The second parameter is always the name of property that holds nested objects. In above case it would be \'children\'.

The third parameter is an object that you use to find object/objects that you want to find/modify/delete. For example if you\'re looking for object with id equal to 1, then you will pass { id: 1} as the third parameter.

And you can:

  1. findFirst(rootObj, \'children\', { id: 1 }) to find first object with id === 1
  2. findAll(rootObj, \'children\', { id: 1 }) to find all objects with id === 1
  3. findAndDeleteFirst(rootObj, \'children\', { id: 1 }) to delete first matching object
  4. findAndDeleteAll(rootObj, \'children\', { id: 1 }) to delete all matching objects

replacementObj is used as the last parameter in two last methods:

  1. findAndModifyFirst(rootObj, \'children\', { id: 1 }, { id: 2, name: \'newObj\'}) to change first found object with id === 1 to the { id: 2, name: \'newObj\'}
  2. findAndModifyAll(rootObj, \'children\', { id: 1 }, { id: 2, name: \'newObj\'}) to change all objects with id === 1 to the { id: 2, name: \'newObj\'}