[javascript] JavaScript를 사용하여 JSON 객체 트리의 모든 노드를 순회

JSON 객체 트리를 통과하고 싶지만 해당 라이브러리를 찾을 수 없습니다. 어렵지는 않지만 휠을 재발 명하는 것처럼 느껴집니다.

XML에는 DOM을 사용하여 XML 트리를 순회하는 방법을 보여주는 많은 자습서가 있습니다.



답변

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


답변

JSON 객체는 단순히 자바 스크립트 객체입니다. 이것이 실제로 JSON의 약자 인 JavaScript Object Notation입니다. 따라서 JSON 객체를 순회하지만 일반적으로 Javascript 객체를 “순회”하도록 선택합니다.

ES2017에서는 다음을 수행합니다.

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

객체로 재귀 적으로 내려가는 함수를 항상 작성할 수 있습니다.

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

좋은 출발점이되어야합니다. 그런 코드를 작성하는 것이 훨씬 쉬워지기 때문에 현대적인 자바 스크립트 메소드를 사용하는 것이 좋습니다.


답변

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


답변

여러 가지 사용 사례를 지원하는 JavaScript로 JSON 데이터를 순회하기위한 새로운 라이브러리가 있습니다.

https://npmjs.org/package/traverse

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

모든 종류의 JavaScript 객체와 함께 작동합니다. 심지어주기를 감지합니다.

각 노드의 경로도 제공합니다.


답변

당신이하고 싶은 일에 달려 있습니다. 다음은 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


답변

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

객체를 순회 할 때 :

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


답변

편집 :이 답변의 아래 모든 예제는 @supersan의 요청에 따라 반복기에서 생성 된 새로운 경로 변수를 포함하도록 편집되었습니다 . 경로 변수는 배열의 각 문자열이 원래 소스 오브젝트에서 결과 반복 값에 도달하기 위해 액세스 된 각 키를 나타내는 문자열 배열입니다. 경로 변수는 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"]));

편집 :이 편집 된 답변은 무한 반복 횡단을 해결합니다.

성가신 무한 객체 탐색 중지

이 편집 대답은 여전히 제공된 사용할 수 있습니다 내 원래의 대답의 추가 혜택 중 하나를 제공 발전기 기능을 깨끗하고 간단하게 사용하기 위해서는 반복 가능한 인터페이스를 (사용하여 생각 for of처럼 루프를 for(var a of b)어디에 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);
}

이것으로부터 자신을 보호하기 위해 클로저 내에 세트를 추가 할 수 있으므로 함수가 처음 호출 될 때 본 객체의 메모리를 구축하기 시작하고 이미 본 객체를 만나면 반복을 계속하지 않습니다. 아래 코드 스 니펫이 그렇게하여 무한 반복 사례를 처리합니다.

더 나은-이것은 자기 참조 객체에서 무한 루프되지 않습니다.

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


원래 답변

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

당신은 단지 자신의 열거 속성 (기본적으로 비 프로토 타입 체인 속성을) 원하는 경우에 당신은으로 반복 사용으로 변경할 수 있습니다 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);
}