sourcecode

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

copyscript 2022. 9. 16. 23:04
반응형

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

JSON으로 변환해서 보내고 싶은 큰 오브젝트가 있습니다.하지만 그것은 원형 구조를 가지고 있다.순환 참조가 있으면 던져버리고 문자열로 묶을 수 있는 것을 보내고 싶습니다.그걸 어떻게 하는 거죠?

감사합니다.

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

obj를 다음 항목으로 분류합니다.

{"a":"foo"}

Node.js에서는 util.inspect(개체)사용할 수 있습니다.순환 링크를 자동으로 "Circular"로 바꿉니다.


빌트인(설치 불필요)이지만 Import해야 합니다.

import * as util from 'util' // has no default export
import { inspect } from 'util' // or directly
// or 
var util = require('util')
To use it, simply call
console.log(util.inspect(myObject))

옵션 객체를 검사 대상으로 전달할 수도 있습니다(위 링크 참조).

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


아래 댓글을 읽고 칭찬해 주세요.

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

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

const circularReference = {otherData: 123}
circularReference.myself = circularReference

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

const stringified = JSON.stringify(circularReference, getCircularReplacer())

console.log(stringified)

표시된 값은 배열이 아닌 집합에 저장해야 합니다(리플라이서는 모든 요소에서 호출됨). 따라서 시도할 필요가 없습니다.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

Circular J에 주의해 주세요.SON은 유지보수 전용이며 플랫은 그 후계 모델입니다.

나는 트린다즈의 해결책을 정말 좋아했다. 좀 더 장황하게, 하지만 몇 가지 버그가 있었다.좋아하는 사람도 고쳐줬어요.

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

인쇄하는 오브젝트가 정말 큰 경우(즉, 무한히 큰 경우) 알고리즘을 제한하고 싶습니다.

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

,도 있다는 점에 하세요.JSON.decycleDouglas Crockford에 의해 구현된 방법.그의 사이클을 봐.이를 통해 거의 모든 표준 구조를 문자열화할 수 있습니다.

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

포크에 that that note note note 。JSON.decycle(variable)원본과 동일하게 동작하며, 이 경우 예외가 발생합니다.variableDOM 요소 / DOM dom dom dom dom dom dom 。

「 」를 사용하는 JSON.decycle(variable, true)결과를 되돌릴 수 없다는 사실을 받아들입니다(재순환으로 DOM 노드가 다시 생성되지 않음).DOM 요소는 어느 할 수 .를 들어, 「」의 는,div에는 ID가 됩니다."div#id-of-the-element".

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

@isaacs에서 json-stringify-safe를 체크하는 것을 추천합니다.NPM에서 사용되고 있습니다.

참고로 Node.js를 사용하지 않는 경우 소스 코드의 관련 부분에서 4-27 행을 복사하여 붙여넣기만 하면 됩니다.

설치하는 방법:

$ npm install json-stringify-safe --save

사용 방법:

// Require the thing
var stringify = require('json-stringify-safe');

// Take some nasty circular object
var theBigNasty = {
  a: "foo",
  b: theBigNasty
};

// Then clean it up a little bit
var sanitized = JSON.parse(stringify(theBigNasty));

그 결과:

{
  a: 'foo',
  b: '[Circular]'
}

@Rob 에서 설명한 로 JSON.stringify의 두 stringify()간단한 예시가 필요한 경우 오류, 정규 표현 및 함수를 사람이 읽을 수 있는 문자열로 강제하는 커스텀 리페이서를 작성했습니다.

모든 순환 참조의 키를 모를 때 이 문제에 대한 해결책을 찾고 있는 구글러에게는 JSON.stringify 함수 주위에 래퍼를 사용하여 순환 참조를 제외할 수 있습니다.https://gist.github.com/4653128 에서 샘플스크립트를 참조해 주세요.

솔루션은 기본적으로 배열 내에서 이전에 인쇄된 객체에 대한 참조를 유지하고 값을 반환하기 전에 이를 리페이서 함수로 확인하는 것으로 요약됩니다.이것은 순환 참조를 배제하는 것보다 더 제한적입니다. 왜냐하면 이것은 물체를 두 번 인쇄하는 것을 배제하기 때문입니다. 그 부작용 중 하나는 순환 참조를 피하는 것입니다.

래퍼 예시:

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

    function printOnceReplacer(key, value){
        var printedObjIndex = false;
        printedObjects.forEach(function(obj, index){
            if(obj===value){
                printedObjIndex = index;
            }
        });

        if(printedObjIndex && typeof(value)=="object"){
            return "(see " + value.constructor.name.toLowerCase() + " 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);
}
var a={b:"b"};
a.a=a;
JSON.stringify(preventCircularJson(a));

평가 대상:

"{"b":"b","a":"CIRCULAR_REFERENCE_REMOVED"}"

다음과 같은 기능이 있습니다.

/**
 * Traverses a javascript object, and deletes all circular values
 * @param source object to remove circular references from
 * @param censoredMessage optional: what to put instead of censored values
 * @param censorTheseItems should be kept null, used in recursion
 * @returns {undefined}
 */
function preventCircularJson(source, censoredMessage, censorTheseItems) {
    //init recursive value if this is the first call
    censorTheseItems = censorTheseItems || [source];
    //default if none is specified
    censoredMessage = censoredMessage || "CIRCULAR_REFERENCE_REMOVED";
    //values that have allready apeared will be placed here:
    var recursiveItems = {};
    //initaite a censored clone to return back
    var ret = {};
    //traverse the object:
    for (var key in source) {
        var value = source[key]
        if (typeof value == "object") {
            //re-examine all complex children again later:
            recursiveItems[key] = value;
        } else {
            //simple values copied as is
            ret[key] = value;
        }
    }
    //create list of values to censor:
    var censorChildItems = [];
    for (var key in recursiveItems) {
        var value = source[key];
        //all complex child objects should not apear again in children:
        censorChildItems.push(value);
    }
    //censor all circular values
    for (var key in recursiveItems) {
        var value = source[key];
        var censored = false;
        censorTheseItems.forEach(function (item) {
            if (item === value) {
                censored = true;
            }
        });
        if (censored) {
            //change circular values to this
            value = censoredMessage;
        } else {
            //recursion:
            value = preventCircularJson(value, censoredMessage, censorChildItems.concat(censorTheseItems));
        }
        ret[key] = value

    }

    return ret;
}

리페이서와 함께 JSON.stringify 메서드를 사용합니다.상세한 것에 대하여는, 이 메뉴얼을 참조해 주세요.http://msdn.microsoft.com/en-us/library/cc836459%28v=vs.94%29.aspx

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

var replacement = {"b":undefined};

alert(JSON.stringify(obj,replacement));

교환 어레이에 주기적인 참조를 추가하는 방법을 알아냅니다.메서드 유형을 사용하여 속성이 'object'(참조) 유형인지 확인하고 정확한 등식 검사( === )를 통해 순환 참조를 확인할 수 있습니다.

한다면

console.log(JSON.stringify(object));

결과:

TypeError: 순환 객체 값

다음에, 다음과 같이 인쇄할 수 있습니다.

var output = '';
for (property in object) {
  output += property + ': ' + object[property]+'; ';
}
console.log(output);

오래된 질문인 것은 알지만, 스마트 서클이라고 하는 NPM 패키지를 제안하고 싶습니다.이 패키지는 다른 방법과는 다르게 동작합니다.크고 깊은 물체를 사용할 때 특히 유용합니다.

기능에는 다음과 같은 것이 있습니다.

  • 오브젝트 내의 순환 참조 또는 단순한 반복 구조를 오브젝트의 첫 번째 발생 경로로 대체한다(문자열 [circular]뿐 아니라).

  • 이 패키지는 폭 우선 검색에서 순환을 찾는 것으로, 이 경로를 가능한 한 작게 할 수 있습니다.이것은 매우 크고 깊은 오브젝트를 다룰 때 중요합니다(JSON.stringify의 커스텀 치환은 DFS를 실시합니다).

  • 오브젝트의 중요도가 낮은 부분을 간단하게 또는 무시할 수 있는 맞춤형 교환이 가능합니다.

  • 마지막으로 패스는 참조된 필드에 액세스하는 데 필요한 방법으로 정확하게 작성되므로 디버깅에 도움이 됩니다.

JSON.stringify()의 두 번째 인수에서는 데이터 내에서 발견된 모든 개체에서 보존해야 하는 키 이름의 배열을 지정할 수도 있습니다.이 방법이 모든 사용 사례에 적용되는 것은 아니지만 훨씬 더 간단한 솔루션입니다.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

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

var json = JSON.stringify(obj, ['a']);
console.log(json);
// {"a":"foo"}

주의: 이상하게도 OP의 객체 정의는 최신 Chrome 또는 Firefox에서 순환 참조 오류를 발생시키지 않습니다.이 답변의 정의는 오류가 발생하도록 수정되었습니다.


이 코드는 순환 참조에 실패합니다.

    JSON.stringify(circularReference);
// TypeError: cyclic object value

다음 코드를 사용합니다.

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

는 github에서 circular-json 라이브러리를 찾았고 그것은 내 문제에 잘 작용했다.

도움이 되는 몇 가지 좋은 기능

  • 멀티플랫폼 사용을 지원하지만 지금까지는 node.js로만 테스트했습니다.
  • API는 동일하기 때문에 JSON을 대체하여 포함 및 사용하기만 하면 됩니다.
  • 자체 구문 분석 방법이 있으므로 '원형' 직렬화된 데이터를 개체로 다시 변환할 수 있습니다.

방식을 되지 않지만 단순함)을 갱신하려면 JSON을 사용하지 circular-json(서양속담, 친구속담)후계자 플레이트

https://www.npmjs.com/package/flatted

위의 오래된 답변에서 @user1541685에서 빌렸는데 새로운 답변으로 교체했습니다.

npm i --save flatted

js 파일에서

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

이 문제는 다음과 같이 해결합니다.

var util = require('util');

// Our circular object
var obj = {foo: {bar: null}, a:{a:{a:{a:{a:{a:{a:{hi: 'Yo!'}}}}}}}};
obj.foo.bar = obj;

// Generate almost valid JS object definition code (typeof string)
var str = util.inspect(b, {depth: null});

// Fix code to the valid state (in this example it is not required, but my object was huge and complex, and I needed this for my case)
str = str
    .replace(/<Buffer[ \w\.]+>/ig, '"buffer"')
    .replace(/\[Function]/ig, 'function(){}')
    .replace(/\[Circular]/ig, '"Circular"')
    .replace(/\{ \[Function: ([\w]+)]/ig, '{ $1: function $1 () {},')
    .replace(/\[Function: ([\w]+)]/ig, 'function $1(){}')
    .replace(/(\w+): ([\w :]+GMT\+[\w \(\)]+),/ig, '$1: new Date("$2"),')
    .replace(/(\S+): ,/ig, '$1: null,');

// Create function to eval stringifyed code
var foo = new Function('return ' + str + ';');

// And have fun
console.log(JSON.stringify(foo(), null, 4));

이것을 시험해 보세요.

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

var circular_replacer = (value) => {
    var seen = [];
    if (value != null && typeof value == "object") {
        if (seen.indexOf(value) >= 0) return;
        seen.push(value);
    }
    return value;
};

obj = circular_replacer(obj);

만, 전에 .delete환입니니다다

delete obj.b; 
const jsonObject = JSON.stringify(obj);

delete 연산자

이렇게 하면 순환 참조를 제거하기 위해 복잡한 로직을 구축하거나 유지할 필요가 없어집니다.

function myStringify(obj, maxDeepLevel = 2) {
  if (obj === null) {
    return 'null';
  }
  if (obj === undefined) {
    return 'undefined';
  }
  if (maxDeepLevel < 0 || typeof obj !== 'object') {
    return obj.toString();
  }
  return Object
    .entries(obj)
    .map(x => x[0] + ': ' + myStringify(x[1], maxDeepLevel - 1))
    .join('\r\n');
}

이 스레드에 있는 대부분의 답변은 다음과 같이 사용할 수 있습니다.JSON.stringify(콜링)JSON.parse재할당이 필요하며 퍼포먼스에 더 큰 영향을 미칩니다.)

소스 오브젝트 트리에서 순환 참조를 삭제하려면 https://stackoverflow.com/a/63952549/2441655 등의 함수를 사용할 수 있습니다.

순환 를 사용하여 에 민감한에 후속 콜을 할 수 예: ).JSON.stringify safe:) safe:

const objTree = {normalProp: true};
objTree.selfReference = objTree;
RemoveCircularLinks(objTree); // without this line, the JSON.stringify call errors
console.log(JSON.stringify(objTree));

다음과 같은 솔루션이 있습니다.

  • 는 사이클만 삭제합니다(여기에 게재된 대부분의 솔루션과 마찬가지로 모든 중복된 오브젝트 참조는 아닙니다).
  • 불필요하게 장황한 것은 아니지만,
  • 빠릅니다.
  • 에는 라이브러리의 의존관계가 필요 없습니다.
function replaceCycles(obj, replacement = undefined, seen = new WeakSet()) {
  if (typeof obj === 'object')
    if (seen.has(obj))
      return replacement 
    else {
      seen.add(obj)
      const newObj = {}
      for (const key in obj)
        newObj[key] = replaceCycles(obj[key], replacement, seen)
      seen.delete(obj)
      return newObj
    }
  else
    return obj
}

사용방법:

const a = {
  b: 'v1',
  c: {
    d: 'v2'
  }
}

a.e = a.c
a.c.f = a.c

console.log(JSON.stringify(replaceCycles(a, '[CYCLE]')))

출력:

"{'b':'v1','c':{'d':'v2','f':'[CYCLE]'},'e':{'d':'v2','f':'[CYCLE]'}}"

이러한 종류의 오브젝트에서 이 문제를 해결하기 위한 또 다른 해결책은 이 라이브러리를 사용하는 것입니다.

https://github.com/ericmuyser/stringy

간단한 절차로 해결할 수 있습니다.

그 외의 회답에 근거해, 다음과 같은 코드로 끝납니다.순환 참조나 커스텀 컨스트럭터를 사용한 오브젝트에서는 매우 잘 동작합니다.

연속화할 특정 객체에서

  • 개체를 이동하는 동안 발견한 모든 개체를 캐시하고 각 개체에 고유한 해시를 할당합니다.ID(자동 증가 번호도 기능합니다)
  • 순환 참조가 발견되면 새 개체의 해당 필드를 원형으로 표시하고 해시를 저장합니다.애트리뷰트로서의 원래 오브젝트의 ID.

Github 링크 - Decycled J아들

DJSHelper = {};
DJSHelper.Cache = [];
DJSHelper.currentHashID = 0;
DJSHelper.ReviveCache = [];

// DOES NOT SERIALIZE FUNCTION
function DJSNode(name, object, isRoot){
    this.name = name;
    // [ATTRIBUTES] contains the primitive fields of the Node
    this.attributes = {};

    // [CHILDREN] contains the Object/Typed fields of the Node
    // All [CHILDREN] must be of type [DJSNode]
    this.children = []; //Array of DJSNodes only

    // If [IS-ROOT] is true reset the Cache and currentHashId
    // before encoding
    isRoot = typeof isRoot === 'undefined'? true:isRoot;
    this.isRoot = isRoot;
    if(isRoot){
        DJSHelper.Cache = [];
        DJSHelper.currentHashID = 0;

        // CACHE THE ROOT
        object.hashID = DJSHelper.currentHashID++;
        DJSHelper.Cache.push(object);
    }

    for(var a in object){
        if(object.hasOwnProperty(a)){
            var val = object[a];

            if (typeof val === 'object') {
                // IF OBJECT OR NULL REF.

                /***************************************************************************/
                // DO NOT REMOVE THE [FALSE] AS THAT WOULD RESET THE [DJSHELPER.CACHE]
                // AND THE RESULT WOULD BE STACK OVERFLOW
                /***************************************************************************/
                if(val !== null) {
                    if (DJSHelper.Cache.indexOf(val) === -1) {
                        // VAL NOT IN CACHE
                        // ADD THE VAL TO CACHE FIRST -> BEFORE DOING RECURSION
                        val.hashID = DJSHelper.currentHashID++;
                        //console.log("Assigned", val.hashID, "to", a);
                        DJSHelper.Cache.push(val);

                        if (!(val instanceof Array)) {
                            // VAL NOT AN [ARRAY]
                            try {
                                this.children.push(new DJSNode(a, val, false));
                            } catch (err) {
                                console.log(err.message, a);
                                throw err;
                            }
                        } else {
                            // VAL IS AN [ARRAY]
                            var node = new DJSNode(a, {
                                array: true,
                                hashID: val.hashID // HashID of array
                            }, false);
                            val.forEach(function (elem, index) {
                                node.children.push(new DJSNode("elem", {val: elem}, false));
                            });
                            this.children.push(node);
                        }
                    } else {
                        // VAL IN CACHE
                        // ADD A CYCLIC NODE WITH HASH-ID
                        this.children.push(new DJSNode(a, {
                            cyclic: true,
                            hashID: val.hashID
                        }, false));
                    }
                }else{
                    // PUT NULL AS AN ATTRIBUTE
                    this.attributes[a] = 'null';
                }
            } else if (typeof val !== 'function') {
                // MUST BE A PRIMITIVE
                // ADD IT AS AN ATTRIBUTE
                this.attributes[a] = val;
            }
        }
    }

    if(isRoot){
        DJSHelper.Cache = null;
    }
    this.constructorName = object.constructor.name;
}
DJSNode.Revive = function (xmlNode, isRoot) {
    // Default value of [isRoot] is True
    isRoot = typeof isRoot === 'undefined'?true: isRoot;
    var root;
    if(isRoot){
        DJSHelper.ReviveCache = []; //Garbage Collect
    }
    if(window[xmlNode.constructorName].toString().indexOf('[native code]') > -1 ) {
        // yep, native in the browser
        if(xmlNode.constructorName == 'Object'){
            root = {};
        }else{
            return null;
        }
    }else {
        eval('root = new ' + xmlNode.constructorName + "()");
    }

    //CACHE ROOT INTO REVIVE-CACHE
    DJSHelper.ReviveCache[xmlNode.attributes.hashID] = root;

    for(var k in xmlNode.attributes){
        // PRIMITIVE OR NULL REF FIELDS
        if(xmlNode.attributes.hasOwnProperty(k)) {
            var a = xmlNode.attributes[k];
            if(a == 'null'){
                root[k] = null;
            }else {
                root[k] = a;
            }
        }
    }

    xmlNode.children.forEach(function (value) {
        // Each children is an [DJSNode]
        // [Array]s are stored as [DJSNode] with an positive Array attribute
        // So is value

        if(value.attributes.array){
            // ITS AN [ARRAY]
            root[value.name] = [];
            value.children.forEach(function (elem) {
                root[value.name].push(elem.attributes.val);
            });
            //console.log("Caching", value.attributes.hashID);
            DJSHelper.ReviveCache[value.attributes.hashID] = root[value.name];
        }else if(!value.attributes.cyclic){
            // ITS AN [OBJECT]
            root[value.name] = DJSNode.Revive(value, false);
            //console.log("Caching", value.attributes.hashID);
            DJSHelper.ReviveCache[value.attributes.hashID] = root[value.name];
        }
    });

    // [SEPARATE ITERATION] TO MAKE SURE ALL POSSIBLE
    // [CYCLIC] REFERENCES ARE CACHED PROPERLY
    xmlNode.children.forEach(function (value) {
        // Each children is an [DJSNode]
        // [Array]s are stored as [DJSNode] with an positive Array attribute
        // So is value

        if(value.attributes.cyclic){
            // ITS AND [CYCLIC] REFERENCE
            root[value.name] = DJSHelper.ReviveCache[value.attributes.hashID];
        }
    });

    if(isRoot){
        DJSHelper.ReviveCache = null; //Garbage Collect
    }
    return root;
};

DecycledJSON = {};
DecycledJSON.stringify = function (obj) {
    return JSON.stringify(new DJSNode("root", obj));
};
DecycledJSON.parse = function (json, replacerObject) {
    // use the replacerObject to get the null values
    return DJSNode.Revive(JSON.parse(json));
};
DJS = DecycledJSON;

사용 예 1:

var obj = {
    id:201,
    box: {
        owner: null,
        key: 'storm'
    },
    lines:[
        'item1',
        23
    ]
};

console.log(obj); // ORIGINAL

// SERIALIZE AND THEN PARSE
var jsonObj = DJS.stringify(obj);
console.log(DJS.parse(jsonObj));

사용 예 2:

// PERSON OBJECT

function Person() {
    this.name = null;
    this.child = null;
    this.dad = null;
    this.mom = null;
}
var Dad = new Person();
Dad.name = 'John';
var Mom = new Person();
Mom.name = 'Sarah';
var Child = new Person();
Child.name = 'Kiddo';

Dad.child = Mom.child = Child;
Child.dad = Dad;
Child.mom = Mom;

console.log(Child); // ORIGINAL

// SERIALIZE AND THEN PARSE
var jsonChild = DJS.stringify(Child);
console.log(DJS.parse(jsonChild));

이 질문이 오래되고 좋은 답변이 많다는 것을 알지만 새로운 맛(es5+) 때문에 이 답변을 올립니다.

Object.defineProperties(JSON, {
  refStringify: {
    value: function(obj) {

      let objMap = new Map();
      let stringified = JSON.stringify(obj,
        function(key, value) {

          // only for objects
          if (typeof value == 'object') {
            // If has the value then return a reference to it
            if (objMap.has(value))
              return objMap.get(value);

            objMap.set(value, `ref${objMap.size + 1}`);
          }
          return value;
        });
      return stringified;
    }
  },
  refParse: {
    value: function(str) {

      let parsed = JSON.parse(str);
      let objMap = _createObjectMap(parsed);
      objMap.forEach((value, key) => _replaceKeyWithObject(value, key));
      return parsed;
    }
  },
});

// *************************** Example
let a = {
  b: 32,
  c: {
    get a() {
        return a;
      },
      get c() {
        return a.c;
      }
  }
};
let stringified = JSON.refStringify(a);
let parsed = JSON.refParse(stringified, 2);
console.log(parsed, JSON.refStringify(parsed));
// *************************** /Example

// *************************** Helper
function _createObjectMap(obj) {

  let objMap = new Map();
  JSON.stringify(obj, (key, value) => {
    if (typeof value == 'object') {
      if (objMap.has(value))
        return objMap.get(value);
      objMap.set(value, `ref${objMap.size + 1}`);

    }
    return value;
  });
  return objMap;
}

function _replaceKeyWithObject(key, obj, replaceWithObject = obj) {

  Object.keys(obj).forEach(k => {

    let val = obj[k];
    if (val == key)
      return (obj[k] = replaceWithObject);
    if (typeof val == 'object' && val != replaceWithObject)
      _replaceKeyWithObject(key, val, replaceWithObject);
  });
}

JSON 파서 라이브러리 treedoc을 사용해 볼 수 있습니다.순환 참조를 지원하고 반복된 객체를 참조와 함께 중복 제외합니다.

yarn add treedoc

import {TD} from 'treedoc'
TD.stringify(obj);

추가 커스터마이즈가 필요한 경우

import {TD, TDEncodeOption} from 'treedoc'

const opt = new TDEncodeOption();
opt.coderOption.setShowType(true).setShowFunction(true);
opt.jsonOption.setIndentFactor(2);
return TD.stringify(obj, opt);

생성된 JSON 파일은 뷰어 http://treedoc.org에서 볼 수 있습니다.이 파일은 JSON 노드 참조를 통한 네비게이션을 지원합니다.

[뻔뻔한 플러그] 제가 이 도서관의 저자입니다

Logging Utilities 클래스에 대해 다음 메서드를 만들었습니다.다음 메서드는 소스 및 대상 개체를 가져와서 지정된 maxLevel을 기준으로 대상에 소스를 할당합니다.

  static assignObjectByLevel(
    sourceObject: any,
    targetObject: any,
    currentLevel: number = 0,
    maxLevel: number = 3,
    showUndefinedValues = false
  ): any {
    if (currentLevel >= maxLevel) {
      return;
    }

    const objQueue = [];
    for (const key in sourceObject) {
      if (sourceObject.hasOwnProperty(key)) {
        const value = sourceObject[key];
        if (typeof value === "object") {
          objQueue.push({ key, value });
        } else {
          targetObject[key] = value;
        }
      } else {
        if (showUndefinedValues) {
          targetObject[key] = "undefined/null";
        }
      }
    }

    while (objQueue.length > 0) {
      const objVal = objQueue.pop();
      currentLevel++;
      targetObject[objVal.key] = {};
      this.assignObjectByLevel(
        objVal.value,
        targetObject[objVal.key],
        currentLevel,
        maxLevel,
        false
      );
    }
  }

사용 예:

   const logObjParam = {
      level1: "value1",
      level2: {
        value2: "value2",
        level3: {
          value3: "value3",
          level4: {
            value4: " value4",
            level5: {
              value5: " value5",
            },
          },
        },
      },
    };

 let logObj = {};
 this.assignObjectByLevel(logObjParam, logObj);

결과:

{
  "level1": "value1",
  "level2": {
    "value2": "value2",
    "level3": {
      "value3": "value3",
      "level4": {}
    }
  }
}

슈퍼시리얼은 JavaScript 객체를 완전히 시리얼화합니다.

https://github.com/denostack/superserial

사용방법:

const serializer = new Serializer();

const nodes = [{ self: null as any, siblings: [] as any[] }, {
  self: null as any,
  siblings: [] as any[],
}];
nodes[0].self = nodes[0];
nodes[0].siblings = nodes;
nodes[1].self = nodes[1];
nodes[1].siblings = nodes;

const serialized = serializer.serialize(nodes);

console.log(serialized);

출력:

[$1,$2];{"self":$1,"siblings":$0};{"self":$2,"siblings":$0}

솔루션에서는, 유저 2451227이 보고한 문제를 수정하고 있습니다.(when o = {}; JSON.stringify([o, o], getCircularReplacer())를 참조해 주세요.

function newCircularReplacer () {
  const seenValues = []
  return circularReplacer

  function circularReplacer (key, value) {
    if (typeof value === 'object' && value !== null && Object.keys(value).length) {
      const stackSize= seenValues.length
      if (stackSize) {
        for (let n = stackSize - 1; seenValues[n][key] !== value; --n)
          seenValues.pop() // clean up expired references
        if (seenValues.includes(value)) return '[Circular]'
      }
      seenValues.push(value)
    }
    return value
  }
}

let o = {a: 1}
o.b = o // Circular reference
console.log(
  JSON.stringify(o, newCircularReplacer()) // {a:1,b:[Circular]} ✅
)

o = {}
a = [o, o] // NOT circular reference
console.log(
  JSON.stringify(a, newCircularReplacer()) // [{},{}] ✅
)

언급URL : https://stackoverflow.com/questions/11616630/how-can-i-print-a-circular-structure-in-a-json-like-format

반응형