140 javascript json
我想遍历一个JSON对象树,但找不到任何库.这似乎并不困难,但感觉就像重新发明轮子一样.
在XML中,有很多教程展示了如何使用DOM遍历XML树:(
The*_*ppo 212
如果你认为jQuery 对于这样一个原始任务有点过分,你可以这样做:
//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);
Run Code Online (Sandbox Code Playgroud)
Eli*_*ght 63
JSON对象只是一个Javascript对象.这实际上就是JSON所代表的:JavaScript Object Notation.因此,您将遍历JSON对象,但是您通常会选择"遍历"Javascript对象.
在ES2017中你会做:
Object.entries(jsonObj).forEach(([key, value]) => {
// do something with key and val
});
Run Code Online (Sandbox Code Playgroud)
您始终可以编写函数以递归方式下降到对象中:
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
}
}
Run Code Online (Sandbox Code Playgroud)
这应该是一个很好的起点.我强烈建议使用现代的javascript方法来处理这类事情,因为它们使得编写这样的代码变得更加容易.
小智 35
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]);
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ben*_*kin 27
有一个新的库,用于通过JavaScript遍历JSON数据,支持许多不同的用例.
https://npmjs.org/package/traverse
https://github.com/substack/js-traverse
它适用于各种JavaScript对象.它甚至可以检测周期.
它也提供了每个节点的路径.
Bri*_*ell 13
取决于你想做什么.这是一个遍历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
Run Code Online (Sandbox Code Playgroud)
如果您正在遍历实际的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)
})
Run Code Online (Sandbox Code Playgroud)
遍历对象时:
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)
})
Run Code Online (Sandbox Code Playgroud)
编辑:此答案中所有下面的示例均已编辑,以包含根据@supersan的请求从迭代器产生的新路径变量。path变量是一个字符串数组,其中数组中的每个字符串代表每个键,该键被访问以从原始源对象获得最终的迭代值。可以将path变量输入lodash的get function / method中。或者,您可以编写自己的lodash的get版本,该版本仅处理如下数组:
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"]));
Run Code Online (Sandbox Code Playgroud)
编辑:此编辑后的答案解决了无限循环遍历。
这个经过编辑的答案仍然提供了我原始答案的附加好处之一,它允许您使用提供的生成器函数来使用更简洁简单的可迭代接口(请考虑使用for of
循环,如for(var a of b)
where b
是可迭代的并且a
是可迭代的元素)。通过使用发电机功能与使它成为一个更简单的API,它也与代码重用帮助一起,这样你就不必再重复无处不在,你想迭代深深的一个对象的属性迭代逻辑,同时也使得有可能break
出如果您想提前停止迭代,则返回循环。
我注意到尚未解决且我的原始答案中没有的一件事是,您应该小心地遍历任意(即,任何“随机”集合)对象,因为JavaScript对象可以是自引用的。这创造了无限循环遍历的机会。但是,未修改的JSON数据不能自引用,因此,如果您使用JS对象的此特定子集,则不必担心无限循环遍历,您可以参考我的原始答案或其他答案。这是一个无休止遍历的示例(请注意,这不是一段可运行的代码,因为否则它将导致您的浏览器选项卡崩溃)。
同样在编辑示例中的生成器对象中,我选择使用,Object.keys
而不是for in
仅迭代对象上的非原型键。如果您希望包含原型密钥,则可以自己替换掉。有关Object.keys
和的两种实现,请参见下面的原始答案部分for in
。
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
// this self-referential property assignment is the only edited line
// from the below original example which makes the traversal
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
function* traverse(o, path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
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] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Run Code Online (Sandbox Code Playgroud)
为了避免这种情况,您可以在闭包内添加一个集合,这样,在首次调用该函数时,它将开始为已看到的对象建立内存,并且一旦遇到已看到的对象就不会继续迭代。下面的代码段可以做到这一点,从而处理无限循环的情况。
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
// this self-referential property assignment is the only edited line
// from the below original example which makes more naive traversals
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
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];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* innerTraversal(o[i], itemPath);
}
}
}
yield* innerTraversal(o);
}
console.log(o);
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Run Code Online (Sandbox Code Playgroud)
如果您不介意丢弃IE并且主要支持更多最新浏览器,那么这是更新的方法(请检查kangax的es6表是否具有兼容性)。您可以为此使用es2015 生成器。我已经相应更新了@TheHippo的答案。当然,如果您确实需要IE支持,则可以使用babel JavaScript Transpiler。
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
function* traverse(o, path=[]) {
for (var i in o) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
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] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Run Code Online (Sandbox Code Playgroud)
如果只希望拥有自己的可枚举属性(基本上是非原型链属性),则可以使用Object.keys
和for...of
循环将其更改为迭代:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
function* traverse(o,path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
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] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Run Code Online (Sandbox Code Playgroud)