我想遍历JSON对象树,但是找不到任何库。看起来似乎并不困难,但是感觉就像是在重新发明轮子。

评论

使迭代器IIFE成为github.com/eltomjan/ETEhomeTools/blob/master/HTM_HTA/…,它具有预定义的(基本)DepthFirst和BreadthFirst,并且能够在JSON结构内部移动而无需递归。

#1 楼

如果您认为jQuery对于这样的原始任务有点过头了,则可以执行以下操作:


评论


为什么选择fund.apply(this,...)?不应该是func.apply(o,...)吗?

–克雷格·塞莱斯特(Craig Celeste)
2014年1月23日下午1:39

@ParchedSquid否。如果您查看apply()的API文档,则第一个参数是目标函数中的this值,而o应该是该函数的第一个参数。虽然将其设置为此(这将是遍历函数)有点奇怪,但是它并不像进程始终使用this引用一样。它也可能为空。

– Thor84no
2015年5月13日14:14

对于严格模式下的jshint,尽管您可能需要在// jshint validthis上添加:true * / func.apply(this,[i,o [i]]);为避免错误W040:可能严格违反。使用此引起的

–Jasdeep Khalsa
15年7月3日在13:37

@jasdeepkhalsa:是的。但是在撰写答案之时,jshint甚至还没有开始为期一年半的项目。

–The河马
15年7月3日在17:14

@Vishal,您可以在遍历函数中添加一个3参数来跟踪深度。 Wenn调用将1递归加到当前级别。

–The河马
16年7月15日在12:21

#2 楼

JSON对象只是一个Javascript对象。实际上,这就是JSON所代表的含义:JavaScript对象表示法。因此,您将遍历一个JSON对象,但是通常选择“遍历”一个Javascript对象。

在ES2017中,您可以执行以下操作:


您总是可以编写一个函数来递归地降级到对象中:我强烈建议对此类事情使用现代javascript方法,因为它们使编写此类代码变得更加容易。

评论


避免遍历(v)其中v == null,因为(typeof null ==“ object”)=== true。函数traverse(jsonObj){if(jsonObj && typeof jsonObj ==“ object”){...

– Marcelo Amorim
17年5月26日在20:16



我讨厌听起来很古怪,但是我认为对此已经有很多困惑,因此为了清楚起见,我要说以下。 JSON和JavaScript对象不是同一件事。 JSON基于JavaScript对象的格式,但是JSON只是符号。它是代表对象的字符串。可以将所有JSON“解析”为JS对象,但是并非所有JS对象都可以“字符串化”为JSON。例如,自引用JS对象不能被字符串化。

–约翰
19年2月5日在22:24

#3 楼

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


评论


您能解释一下为什么更好吗?

–拉斐尔·赫斯科维奇(Rafael Herscovici)
2014年7月28日在7:53

如果该方法打算执行除日志以外的任何操作,则应检查是否为null,null仍然是一个对象。

–wi1
2014年8月29日在4:52

@ wi1同意您的意见,可以检查!! o [i] && typeof o [i] =='object'

– pilau
2015年2月2日,12:09

#4 楼

有一个新的库用于使用JavaScript遍历JSON数据,该库支持许多不同的用例。

https://npmjs.org/package/traverse

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

它适用于各种JavaScript对象。它甚至可以检测周期。

它也提供每个节点的路径。

评论


js遍历似乎也可以通过node.js中的npm获得。

–维尔
2012年12月27日在21:20

是。它在那里被称为遍历。他们有一个可爱的网页!更新我的答案以包含它。

–本杰明·阿特金(Benjamin Atkin)
2012-12-27 23:31:

#5 楼

取决于您要做什么。这是遍历JavaScript对象树,按顺序打印键和值的示例:

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 楼

如果您要遍历实际的JSON字符串,则可以使用reviver函数。

当遍历对象时:

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


#7 楼

原始的简化答案
如果您不介意丢弃IE并且主要支持更多当前浏览器,请使用更新的方式(请查看kangax的es6表是否兼容)。您可以为此使用es2015生成器。我已经相应地更新了@TheHippo的答案。当然,如果您确实需要IE支持,则可以使用babel JavaScript transpiler。



 // Implementation of Traverse
function* traverse(o, path=[]) {
    for (var i in o) {
        const itemPath = path.concat(i);
        yield [i,o[i],itemPath,o];
        if (o[i] !== null && typeof(o[i])=="object") {
            //going one step down in the object tree!!
            yield* traverse(o[i], itemPath);
        }
    }
}

// Traverse usage:
//that's all... no magic, no bloated framework
for(var [key, value, path, parent] of traverse({ 
    foo:"bar",
    arr:[1,2,3],
    subo: {
        foo2:"bar2"
    }
})) {
  // do something here with each key and value
  console.log(key, value, path, parent);
} 




如果只希望拥有自己的可枚举属性(基本上是非原型链属性),则可以使用Object.keysfor...of循环将其更改为迭代:



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

//that's all... no magic, no bloated framework
for(var [key, value, path, parent] of traverse({ 
    foo:"bar",
    arr:[1,2,3],
    subo: {
        foo2:"bar2"
    }
})) {
  // do something here with each key and value
  console.log(key, value, path, parent);
} 




编辑:此经过编辑的答案解决了无限循环遍历。提供的生成器功能,以便使用更简洁简单的可迭代接口(请考虑使用for of循环,如for(var a of b)所示,其中b是可迭代的,而a是可迭代的元素)。通过使用生成器函数以及一个更简单的api,它还可以帮助实现代码重用,因此您不必在要深入迭代对象属性的任何地方重复迭代逻辑,这也使得break成为可能。如果您想更早地停止迭代,请执行循环操作。
我注意到尚未解决的一件事,也是我最初的回答中没有提到的一件事,是您应谨慎遍历任意(即任何“随机”对象)对象,因为JavaScript对象可以是自引用的。这创造了无限循环遍历的机会。但是,未修改的JSON数据不能自引用,因此,如果您使用JS对象的此特定子集,则不必担心无限循环遍历,您可以参考我的原始答案或其他答案。这是一个无休止的遍历示例(请注意,它不是一段可运行的代码,因为否则它会导致浏览器选项卡崩溃)。 Object.keys的参数,它仅迭代对象上的非原型键。如果您希望包含原型密钥,则可以自己换掉。请参阅下面的原始答案部分,以了解for inObject.keys的两种实现。
更糟-这将对自引用对象进行无限循环: pre>
要避免这种情况,您可以在闭包内添加一个集合,这样,在首次调用该函数时,它将开始建立它所看到的对象的内存,并且一旦遇到该对象就不会继续迭代已经看到的对象。下面的代码段可以做到这一点,从而处理无限循环的情况。
更好-这不会对自引用对象造成无限循环:



 for in 





编辑:此答案中的所有上述示例均已编辑,以包括根据@supersan的请求从迭代器产生的新路径变量。 path变量是一个字符串数组,其中数组中的每个字符串代表每个键,该键被访问以从原始源对象获得最终的迭代值。可以将路径变量输入lodash的get函数/方法中。或者,您可以编写自己的lodash的get版本,该版本仅处理如下数组:



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

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

// this self-referential property assignment is the only real logical difference 
// from the above original example which ends up making this naive traversal 
// non-terminating (i.e. it makes it infinite loop)
o.o = o;

//that's all... no magic, no bloated framework
for(var [key, value, path, parent] of traverse(o)) {
  // do something here with each key and value
  console.log(key, value, path, parent);
}
 




您还可以像这样设置集合函数:



 function* traverse(o) {
  const memory = new Set();
  function * innerTraversal (o, path=[]) {
    if(memory.has(o)) {
      // we've seen this object before don't iterate it
      return;
    }
    // add the new object to our memory.
    memory.add(o);
    for (var i of Object.keys(o)) {
      const itemPath = path.concat(i);
      yield [i,o[i],itemPath, o]; 
      if (o[i] !== null && typeof(o[i])=="object") {
        //going one step down in the object tree!!
        yield* innerTraversal(o[i], itemPath);
      }
    }
  }
  yield* innerTraversal(o);
}

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

/// this self-referential property assignment is the only real logical difference 
// from the above original example which makes more naive traversals 
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
    
console.log(o);
//that's all... no magic, no bloated framework
for(var [key, value, path, parent] of traverse(o)) {
  // do something here with each key and value
  console.log(key, value, path, parent);
} 




编辑2020年9月:我添加了一个父级,以更快地访问上一个对象。这样可以使您更快地构建反向遍历器。另外,您总是可以修改遍历算法以进行广度优先搜索,而不是深度优先,实际上这可能更可预测,实际上这是带有广度优先搜索的TypeScript版本。由于这是一个JavaScript问题,因此我将JS版本放在这里:



 function get (object, path) {
  return path.reduce((obj, pathItem) => obj ? obj[pathItem] : undefined, object);
}

const example = {a: [1,2,3], b: 4, c: { d: ["foo"] }};
// these paths exist on the object
console.log(get(example, ["a", "0"]));
console.log(get(example, ["c", "d", "0"]));
console.log(get(example, ["b"]));
// these paths do not exist on the object
console.log(get(example, ["e", "f", "g"]));
console.log(get(example, ["b", "f", "g"])); 




评论


好答案!是否可以为遍历的每个键返回a.b.c,a.b.c.d等路径?

–山桑
19年5月24日在23:43

@supersan,您可以查看我的更新代码段。我为每个字符串数组添加了一个路径变量。数组中的字符串表示从原始源对象获得的最终迭代值所访问的每个键。

–约翰
19年6月11日在22:22

#8 楼

我想在匿名函数中使用@TheHippo的完美解决方案,而不使用进程和触发器函数。以下内容对我有用,并为像我这样的新手程序员提供了共享。

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


#9 楼

大多数Javascript引擎不会优化尾部递归(如果您的JSON未深度嵌套,这可能不是问题),但我通常会谨慎行事,而是进行迭代,例如, br />

#10 楼

我创建了一个库来遍历和编辑深层嵌套的JS对象。在此处查看API:https://github.com/dominik791

您也可以使用演示应用程序与库进行交互:
https://dominik791.github.io/obj- traverse-demo /

用法示例:
您应该始终具有根对象,该对象是每种方法的第一个参数:

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


第二个参数始终是包含嵌套对象的属性的名称。在上述情况下,它将是'children'。第三个参数是用于查找要查找/修改/删除的一个或多个对象的对象。例如,如果要查找id等于1的对象,则将传递{ id: 1}作为第三个参数。

您可以:



findFirst(rootObj, 'children', { id: 1 })查找带有id === 1的第一个对象



findAll(rootObj, 'children', { id: 1 })查找所有带有id === 1的对象




findAndDeleteFirst(rootObj, 'children', { id: 1 })删除第一个匹配的对象

findAndDeleteAll(rootObj, 'children', { id: 1 })删除所有匹配的对象

replacementObj在最后两个方法中用作最后一个参数:



findAndModifyFirst(rootObj, 'children', { id: 1 }, { id: 2, name: 'newObj'})将带有id === 1的第一个找到的对象更改为{ id: 2, name: 'newObj'}


findAndModifyAll(rootObj, 'children', { id: 1 }, { id: 2, name: 'newObj'})将带有id === 1的所有对象更改为{ id: 2, name: 'newObj'}

#11 楼

我的脚本:

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


输入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": []
                    }
                ]
            }
       ]
   }
]


函数调用:

callback_func(inp_json);


根据我的需要输出:

["output/f1/ver"]


#12 楼




 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> 




评论


使其提交表单enctype application / json

–承
17年11月24日在12:39

#13 楼

我们将对象扫描用于许多数据处理任务。一旦将头缠住它,它就会很强大。这是基本遍历的方法



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

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

objectScan(['**'], {
  filterFn: ({ key, value }) => {
    console.log(key, value);
  }
})(obj);
// => [ 'subo', 'foo2' ] bar2
// => [ 'subo' ] { foo2: 'bar2' }
// => [ 'arr', 2 ] 3
// => [ 'arr', 1 ] 2
// => [ 'arr', 0 ] 1
// => [ 'arr' ] [ 1, 2, 3 ]
// => [ 'foo' ] bar 

 .as-console-wrapper {max-height: 100% !important; top: 0} 

 <script src="https://bundle.run/object-scan@13.6.9"></script> 




免责声明:我是object-scan的作者

#14 楼

对我来说最好的解决方案是:

简单且不使用任何框架

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


#15 楼

您可以获取所有键/值,并使用此方法保留层次结构。



#16 楼

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