I'm making a game, and I've come across a problem... When I try to save, JSON fails and reports that circular reference is being made somewhere. I don't think it actually is, I can't see it, so is there an algorithm or anything which could tell me where it is exactly (between which objects and stuff)? Also, is there a JSON alternative that can save circular reference? I'm running a node.js server, I saw this, but I can't get it to work (it's not made as a module i can require() in my code).
相关问题
- Is there a limit to how many levels you can nest i
- Jackson Deserialization not calling deserialize on
- How to maintain order of key-value in DataFrame sa
- How to toggle on Order in ReactJS
- StackExchange API - Deserialize Date in JSON Respo
If you want to serialize a circular reference so you can save it, you need to make the reference "virtual" in that it can't be serialized as a circular reference, since that would cause serialization to serialize the same circle of objects forever (or at least until the runtime has run out of memory).
So instead of storing the circular reference itself, you just store a pointer to the object. The pointer will just be something like
ref : '#path.to.object'
that can be resolved when you deserialize so you point the reference back to the actual object. You just need to break the reference on serialization to be able to serialize it.Discovering a circular reference in JavaScript can be done by recursively iterating through all objects (with
for (x in y)
), storex
in an array and compare eachx
with the identity operator (a.k.a. strict comparison operator)===
for eachz
in the temporary array. Wheneverx === z
equals true, replace the reference tox
with a placeholder that will be serialized to the above mentionedref
.An alternative to keeping an array over "visited" objects is to "taint" the objects you iterate through by setting a property on them, like in this very naïve example:
This is a small extension to Andris' answer that tells you where the first circular element is so you can deal with it accordingly.
If you don't want a string, the tree array is unnecessary. Just change the original function to
for the circular object itself or
for its parent.
I was thinking about what you're trying to accomplish based off the initial code from your other question. Why not do something like this.
That way you don't have to use a circular reference and it accomplishes the same thing.
There is no good way to detect circularity in objects but it is possible though by walking the object tree and checking references. I baked up a node-walking function that tries to detect if a node has been already used as its parent
And the usage would be
isCircularObject(obj_value)
where the function returnstrue
if circularity exists andfalse
if not.The key point being that an object value is equal with an other value only if it is the same object reference and not when it's another object (even if completely similar).
Here is the code that I am using to detect circular references, it uses the technique that was suggested in the accepted answer by asbjornu, whereby each value is walked through and its reference is maintained in an array so that the next value can be compared with those previously walked.
This code is available on jsfiddle, where you can test it for yourself. I have also run some performance tests on jsperf.
Array.indexOf
was only introduced as of Javascript 1.6, see MDN pageArray.isArray
was only introduced as of Javascript 1.8.5, see MDN pageObject.keys
was only introduced as of Javascript 1.8.5, see MDN pageIt is also worth noting that
arguments.callee
is deprecated and forbidden in strict mode in preference to using named functions