简体   繁体   中英

JavaScript: Stringify a shallow copy of a circular referenced object?

Is there a way to get just a shallow copy of the below to only get one layer deep? I have a means to fix this using a completely different design but I was wondering if anyone else had ran into what I am trying to convert to a string before.

var SomeObjClass = function() {
    var self = this;
    this.group = {
        members: [self]
    };
};
var p = new SomeObjClass();
var str = JSON.stringify(p);

It's a little unclear what you're asking, but if your goal is to simply stringify a circular object, you'll have to override toJSON to specify how you'd like your object to be represented

 function SomeObjClass () { var self = this; this.group = { members: [self] }; } SomeObjClass.prototype.addMember = function(m) { this.group.members.push(m); }; // when stringifying this object, don't include `self` in group.members SomeObjClass.prototype.toJSON = function() { var self = this; return { group: { members: self.group.members.filter(function (x) { return x !== self }) } }; } var a = new SomeObjClass(); var b = new SomeObjClass(); a.addMember(b); console.log(JSON.stringify(a))

This is probably the best I can help you without seeing more of your code. I don't know how you're using this code, but whatever the case, this is probably not the best design for your class. If you share the rest of the class and the code that uses it, we can probably help you more effectively.

If you check MDN reference about JSON.stringify , you'll see that it accepts as a second parameter a replacer function. This function is useful to massage a bit the elements that you want stringified.

It could help you to avoid your circular problem.

For instance:

 function avoidCircularReference(obj) { return function(key, value) { return key && typeof value === 'object' && obj === value ? undefined : value; }; } var SomeObjClass = function() { var self = this; this.group = { members: [self, {a:'f', b: [self]}] }; }; var p = new SomeObjClass(); var str = JSON.stringify(p, avoidCircularReference(p)); console.log(str);

However, and as stated in the documentation and shown running the example:

Note: You cannot use the replacer function to remove values from an array. If you return undefined or a function then null is used instead.

So you'd have to deal in some way with these nulls. Anyway, you could play with this function and "adapt" it to your needs. For instance and applied to your example:

 function avoidCircularReference(obj) { var removeMeFromArray = function(arr) { var index = arr.indexOf(obj); if (index > -1) { arr.splice(index, 1); } }; return function(key, value) { if (Object.prototype.toString.call(value) === "[object Array]") { removeMeFromArray(value); } return value; }; } var SomeObjClass = function() { var self = this; this.group = { members: [self, { a: 'f', b: [self] }] }; }; var p = new SomeObjClass(); var str = JSON.stringify(p, avoidCircularReference(p)); console.log(str);

To solve the problem and keep the JSON.stringify simplicity, I use the following approach (here in my dehydrate method)

public dehydrate(): string {
    var seenObjects = [];
    function inspectElement(key, value) {
        if (detectCycle(value)) {
            return '[Ciclical]';
        } else {
            return value;
        };
    };
    function detectCycle(obj): boolean {
        if (obj && (typeof obj == 'object')) {
            for (let r of seenObjects) {
                if (r == obj) {
                    return true;
                };
            };
            seenObjects.push(obj);
        };
        return false;
    };
    let json: string = JSON.stringify(this, inspectElement,'  ');
    return json;
};

Note that although this is TypeScript, using strong types to achieve the results inside the method would lead us to some confusion.

Unfortunately I had to use for instead of array search because it simply didn't work to me.

This is an implementation that worked for me. It relies on reference comparison for equality, which should be fine for this purpose.

I include the index of the offending object in the replacer return value so that in principle, you could restore the cyclic object when deserializing.

 function safeJsonStringify(value) { const visitedObjs = []; function replacerFn(key, obj) { const refIndex = visitedObjs.indexOf(obj); if (refIndex >= 0) return `cyclic-ref:${refIndex}`; if (typeof obj === 'object' && obj !== null) visitedObjs.push(obj); return obj; } return JSON.stringify(value, replacerFn); } // Take it for a spin: const cyclic = { greeting: 'Hello!' }; cyclic.badRef = cyclic; console.log(safeJsonStringify(cyclic));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM