2

I have an JavaScript Object I want to serialize as a String:

key                     {...}       Object
    mandant             "00001"     String
    personalNummer      600235      Number

First I used JSON2 where the return value was undefined. With JSON3 I get an TypeError and the comment in the Line of json3.js says:

// Cyclic structures cannot be serialized by `JSON.stringify`.

The problem seems to result from following lines in json3.js:

// Manually invoke the callback for the `constructor` property due to
// cross-environment inconsistencies.
if (isConstructor || isProperty.call(object, (property = "constructor"))) {
    callback(property);
}

But there should be no cycle and I'm obvious not able to find out what the heck is going on.

When I create the Object by hand while debugging everything works fine.

So what could raise the error?


EDIT:
I succeeded to prepare a scenario to produce the error:

  • It just happens in IE9 with compatibility modes IE7 and IE8 (Firefox 22 is fine, too)
  • It just happens if a new window get opened which references the data from the opener window



*JSON_Cycle.html*:

    <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script>
    <script type="text/javascript" src="http://bestiejs.github.io/json3/lib/json3.js"></script>
    <script>
    var dataGlobal = {mandant: "Hallo Welt!", personalNummer: 123456};
        $(function() {
            window.open("JSON_Cycle_Popup.html", 'popup');
        });
    </script>



*JSON_Cycle_Popup.html*:

<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script type="text/javascript" src="http://bestiejs.github.io/json3/lib/json3.js"></script>
<script>
    var dataGlobal = null;
    $(function() {
        dataGlobal = window.opener.dataGlobal;
        alert(JSON.stringify(dataGlobal));
    });
</script>
6
  • 1
    Can you provide minimal code required to generate the error ? Commented Jul 19, 2013 at 10:37
  • I'd loved to, but the problem doesn't show when creating such objects by hand. Seems it only comes to when the object gets loaded through AJAX ($.post()). On server side I use Jackson to serialize PoJOs. I will set up a little Scenario trying to provoke the error... Commented Jul 22, 2013 at 7:43
  • @MarvinEmilBrach Given that the data is coming back from the server as a JSON string, why are you converting it into an object only to convert it back to a JSON string again? Commented Jul 22, 2013 at 14:38
  • because I use it... I just can't stand to hold it in memory without touching it... Commented Jul 23, 2013 at 6:41
  • @MarvinEmilBrach I just meant request it as text rather than json so it is returned as a string rather than an object. See my updated answer for more details. Commented Jul 23, 2013 at 20:56

2 Answers 2

1
+100

Based on the new information in your question, I can now explain what is happening.

In your JSON_Cycle.html file, add the following line after the dataGlobal variable is initialised.

alert(typeof dataGlobal.constructor);

Now do the same thing in JSON_Cycle_Popup.html. Notice the difference? On IE, the first alert shows the type of the constructor to be a "function", but in the popup window that same constructor returns a type of "object".

So that's the first issue. The next issue arises from the way json3 enumerates properties on an object. They don't just use a regular for loop, i.e. for (property in object) .... They have a lot of code that tries to detect inconsistencies between the different browser implementations to produce an enumerator that returns the same results on all platforms.

One of the effects of this enumerator, is that it returns a constructor property for every object, even if one would not usually have been returned in the for loop. This seems completely unnecessary, since you obviously wouldn't want to serialize the constructor function in a json object anyway. But I suspect this code was inherited from some other project where that behaviour was desireable.

The combination of these two issues, means that when serializing the dataGlobal object, json3 is going to find a constructor property which it will try to process. Then when serializing that constructor (which it thinks is another object), it's going to find another constructor property (again which appears to be an object). This process would continue to infinity, if it weren't for the next issue.

After four levels of constructor nesting, the function pointers cycle. Namely:

dataGlobal.constructor.constructor.constructor.constructor ==
dataGlobal.constructor.constructor

I don't know why that is the case, but it seems to be so on all browsers that I've tested. This means that instead of getting infinite recursion, the json3 code detects a cycle and throws a TypeError exception.

So that's the explanation for your error. A bug in IE combined with unnecessary cleverness on the part of the json3 property enumerator.

Sign up to request clarification or add additional context in comments.

5 Comments

nice answer (would do a much more better question than mine ;)) +1... Today I will take an hour or two to set up a testing scenario and while doing that provide you with more information...
I've tried the fix with delete instead of assigning an empty function. But it doesn't work. The empty function is indeed problematic if there was a constructor defined, but i suspect in this case the problem will not raise at all...
@MarvinEmilBrach I've rewritten my answer explaining what is happening based on the new information you've provided.
Great detailed answer with accurate explanation. So the bounty is yours :)
Thanks. I appreciate the bounty.
1

Before serializing I have to set a constructor-function:

key.constructor = function() {};

EDIT:
But this works only with Objects! With an Array, I have a similar problem. But here the above fix will not work!

In this case while stringifying the actual Array will get packed in an new Array on index 0. Interestingly the constructor of the actual Array is still a function. BUT: there are two additional Objects on indices 1 and 2. and those are objects again. But it seems there are not manipulatable from outside the JSON lib.

I have not the time and nerves to figure it out to the end, or modify and test the JSON. So I use the unelegant method putting the elements into a new one (NOT cloning -> result will be the same error):

$.each(fahrzeuge, function() {
    zugNummern.push(this);
});

Because there are just a few elements it is not expensive on performance and will do it. But I would be glad if anybody will post a better solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.