[javascript] JSON과 같은 형식으로 원형 구조를 인쇄하려면 어떻게해야합니까?

JSON으로 변환하여 보내려는 큰 객체가 있습니다. 그러나 그것은 원형 구조를 가지고 있습니다. 순환 참조가 존재하는 것을 던지고 문자열로 지정할 수있는 것을 보내려고합니다. 어떻게합니까?

감사.

var obj = {
  a: "foo",
  b: obj
}

obj를 다음과 같이 문자열 화하고 싶습니다.

{"a":"foo"}



답변

JSON.stringify맞춤 교체와 함께 사용하십시오 . 예를 들면 다음과 같습니다.

// Demo: Circular reference
var circ = {};
circ.circ = circ;

// Note: cache should not be re-used by repeated calls to JSON.stringify.
var cache = [];
JSON.stringify(circ, (key, value) => {
  if (typeof value === 'object' && value !== null) {
    // Duplicate reference found, discard key
    if (cache.includes(value)) return;

    // Store value in our collection
    cache.push(value);
  }
  return value;
});
cache = null; // Enable garbage collection

이 예에서 교체기는 100 % 정확하지 않습니다 ( “중복”의 정의에 따라 다름). 다음과 같은 경우 값이 삭제됩니다.

var a = {b:1}
var o = {};
o.one = a;
o.two = a;
// one and two point to the same object, but two is discarded:
JSON.stringify(o, ...);

그러나 개념은 다음과 같습니다. 사용자 정의 대체기를 사용하고 구문 분석 된 오브젝트 값을 추적하십시오.

es6로 작성된 유틸리티 기능으로 :

// safely handles circular references
JSON.safeStringify = (obj, indent = 2) => {
  let cache = [];
  const retVal = JSON.stringify(
    obj,
    (key, value) =>
      typeof value === "object" && value !== null
        ? cache.includes(value)
          ? undefined // Duplicate reference found, discard key
          : cache.push(value) && value // Store value in our collection
        : value,
    indent
  );
  cache = null;
  return retVal;
};

// Example:
console.log('options', JSON.safeStringify(options))


답변

Node.js에서 util.inspect (object)를 사용할 수 있습니다 . 원형 링크를 “[Circular]”로 자동 대체합니다.


내장되어 있지만 (설치가 필요하지 않음) 가져와야합니다.

import * as util from 'util' // has no default export
import { inspect } from 'util' // or directly
// or 
var util = require('util')

사용하려면 간단히 전화하십시오.

console.log(util.inspect(myObject))

또한 검사 할 옵션 객체를 전달할 수 있습니다 (위 링크 참조).

inspect(myObject[, options: {showHidden, depth, colors, showProxy, ...moreOptions}])


아래 의견을 읽고 댓글을 달아주세요.


답변

아무도 아직 MDN 페이지에서 적절한 솔루션을 게시하지 않은 이유가 궁금합니다 …

const getCircularReplacer = () => {
  const seen = new WeakSet();
  return (key, value) => {
    if (typeof value === "object" && value !== null) {
      if (seen.has(value)) {
        return;
      }
      seen.add(value);
    }
    return value;
  };
};

JSON.stringify(circularReference, getCircularReplacer());

확인 된 값은 배열이 아닌 세트에 저장해야합니다 ( 모든 요소에서 대체 프로그램이 호출 ) . 체인에서 JSON.stringify 각 요소 를 시도 하여 순환 참조로 이어질 필요는 없습니다 .

허용 된 답변과 마찬가지로이 솔루션은 원형 뿐만 아니라 반복되는 모든 값을 제거 합니다. 그러나 적어도 지수 복잡성은 없습니다.


답변

그냥 해

npm i --save circular-json

그런 다음 js 파일에서

const CircularJSON = require('circular-json');
...
const json = CircularJSON.stringify(obj);

https://github.com/WebReflection/circular-json

참고 :이 패키지와는 아무 관련이 없습니다. 그러나 나는 이것을 위해 사용합니다.

2020 업데이트

CircularJSON은 유지 관리 중이며 flatted 는 후속 버전입니다.


답변

나는 Trindaz의 솔루션을 더 좋아했지만 더 장황했지만 버그가있었습니다. 나는 그것을 좋아하는 사람을 위해 그들을 고쳤다.

또한 캐시 객체에 길이 제한을 추가했습니다.

인쇄중인 객체가 실제로 큰 경우-무한히 큰 의미-알고리즘을 제한하고 싶습니다.

JSON.stringifyOnce = function(obj, replacer, indent){
    var printedObjects = [];
    var printedObjectKeys = [];

    function printOnceReplacer(key, value){
        if ( printedObjects.length > 2000){ // browsers will not print more than 20K, I don't see the point to allow 2K.. algorithm will not be fast anyway if we have too many objects
        return 'object too long';
        }
        var printedObjIndex = false;
        printedObjects.forEach(function(obj, index){
            if(obj===value){
                printedObjIndex = index;
            }
        });

        if ( key == ''){ //root element
             printedObjects.push(obj);
            printedObjectKeys.push("root");
             return value;
        }

        else if(printedObjIndex+"" != "false" && typeof(value)=="object"){
            if ( printedObjectKeys[printedObjIndex] == "root"){
                return "(pointer to root)";
            }else{
                return "(see " + ((!!value && !!value.constructor) ? value.constructor.name.toLowerCase()  : typeof(value)) + " with key " + printedObjectKeys[printedObjIndex] + ")";
            }
        }else{

            var qualifiedKey = key || "(empty key)";
            printedObjects.push(value);
            printedObjectKeys.push(qualifiedKey);
            if(replacer){
                return replacer(key, value);
            }else{
                return value;
            }
        }
    }
    return JSON.stringify(obj, printOnceReplacer, indent);
};


답변

@RobW의 대답은 정확하지만 더 성능이 좋습니다! 해시 맵 / 세트를 사용하기 때문에 :

const customStringify = function (v) {
  const cache = new Set();
  return JSON.stringify(v, function (key, value) {
    if (typeof value === 'object' && value !== null) {
      if (cache.has(value)) {
        // Circular reference found
        try {
          // If this value does not reference a parent it can be deduped
         return JSON.parse(JSON.stringify(value));
        }
        catch (err) {
          // discard key if value cannot be deduped
         return;
        }
      }
      // Store value in our set
      cache.add(value);
    }
    return value;
  });
};


답변

JSON.decycleDouglas Crockford가 구현 한 방법 도 있습니다 . 그의 cycle.js를 참조하십시오
. 이를 통해 거의 모든 표준 구조를 문자열화할 수 있습니다.

var a = [];
a[0] = a;
a[1] = 123;
console.log(JSON.stringify(JSON.decycle(a)));
// result: '[{"$ref":"$"},123]'.

retrocycle메소드를 사용 하여 원본 객체를 다시 만들 수도 있습니다 . 따라서 객체를주기 위해 객체에서 사이클을 제거 할 필요가 없습니다.

그러나 이는 DOM 노드 (실제 사용 사례의 일반적인주기 원인) 에는 작동 하지 않습니다 . 예를 들어 다음과 같은 상황이 발생합니다.

var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a)));

그 문제를 해결하기 위해 포크를 만들었습니다 ( cycle.js fork 참조 ). 이것은 잘 작동합니다 :

var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a, true)));

내 포크 JSON.decycle(variable)에서 원본과 같이 작동하며 variableDOM 노드 / 요소 가 포함되어 있으면 예외가 발생합니다 .

사용 JSON.decycle(variable, true)하면 결과를 되돌릴 수 없다는 사실을 받아들입니다 (복귀 화는 DOM 노드를 다시 만들지 않습니다). DOM 요소는 어느 정도 식별 가능해야합니다. 예를 들어 div요소에 id가 있으면 string으로 대체됩니다 "div#id-of-the-element".