Serializing the value of empty DOM elements using native JSON in IE8

With native JSON support enabled in IE8, users can now take advantage of the built-in JSON.stringify and JSON.parse methods to serialize and deserialize JScript values to JSON text and vice versa. However, there is a known issue in IE8’s native JSON implementation, wherein if a user tries to read the value of an empty DOM element, and serialize the same using native JSON, the result is not the same as a user would expect while serializing "".

Here is a sample code which demonstrates the problem:

    1: var foo = document.createElement("input").value; // foo === ""
    2: var bar = ""; // bar === ""
    3:  
    4: JSON.stringify(foo); // retuns '"null"' 
    5: JSON.stringify(bar); // retuns '""' 

Another similar example is when a user serializes the value from an empty input control, as below.

    1: var foo = document.getElementById('data').value; // no value provided in the 'data' input control
    2: JSON.stringify(foo); // retuns '"null"'

In both the above cases, serialization of foo should have generated '""' instead of '"null"'. This is a bug in the production version of IE8.  The problem here is that within the DOM a special encoding is used to represent a missing string value.  Even though this special value is different from the encoding of the JScript literal "", throughout the JScript implementation the value is treated as being === to "", except for a specific case in JSON.stringify.

Since this special value only originates from accesses to DOM objects, a workaround would be to explicitly censor them on every DOM access that might return one.  For example,

    1: if (foo === "") foo = ""; //ensure that possibly bogus "" is replaced with a real ""
    2: JSON.stringify(foo); // retuns '""'

Also, since the difference is only observable via JSON.stringify, another alternative is to use the replacer function to perform the substitution. For example:

    1: JSON.stringify(foo, function(k, v) { return v === "" ? "" : v });
    2: //the above will return '""', not '"null"'

Either of the above workarounds has the disadvantage that additional code must be written each time the value of an InputElement element is accessed or each time JSON.stringify is called. Another possible workaround that avoids this extra coding is to use the IE8 Mutable DOM Prototype features to patch HTMLInputElement.prototype.value such that the undesired value is filtered out every time value is accessed. For example, consider this HMTL file:

    1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    2: <html xmlns="https://www.w3.org/1999/xhtml" >
    3: <head>
    4:     <title>Test Page</title>
    5:     <script>
    6:     if (JSON.stringify(document.createElement("input").value)==='"null"') {
    7:        //Wrapper HTMLInputElement.prototype.value with a new get accessor
    8:        //that filters special DOM empty string value 
    9:        (function() {
   10:           var builtInInputValue = 
   11:             Object.getOwnPropertyDescriptor(
   12:                   HTMLInputElement.prototype, "value").get;
   13:             Object.defineProperty(HTMLInputElement.prototype, "value",
   14:                   { get: function() {
   15:                      //Call builtin value accessor 
   16:                      var possiblyBad = builtInInputValue.call(this);
   17:                      //Replace DOM empty string with real "" 
   18:                      return possiblyBad === "" ? "" : possiblyBad;    
   19:                      }
   20:              });
   21:            })(); // call anonymous function to install the wrapper
   22:         );
   23:     </script>
   24: </head>
   25: <body>
   26:   <input type="button" value="click to test"
   27:       onclick="alert(JSON.stringify(document.createElement('input').value));" />  
   28: </body>
   29: </html>

In the <head> of this page a test is made to see if the anomalous stringify behavior is observed. If so, HTMLInputElement.prototype.value is modified to correct the problem. Any access to the value property of an Input element within the body of the page, such as the click handler that is shown, will now return the correct "" value.

This explains the anomaly in the IE8 JSON behavior while serializing the value of certain DOM elements and show three different possible workarounds to the problem.

 

Gaurav Seth, Program Manager, JScript

Comments

  • Anonymous
    June 23, 2009
    If "this is a bug in the production version of IE8" then why don't you just roll out an update to IE8 to fix this fullstop rather than giving us all these horrible workarounds..? i can understand the need for the workarounds, but if windows update pushes out a new release of IE8 then within a couple of months, everyone should have the fixed version fullstop..?

  • Anonymous
    June 23, 2009
    Yes, we are working towards a fix for the issue. However, the timeline for a rollout is not certain as of now. Will update as we proceed. The blog aims to explain the behavior that developers might observe, and possible workarounds if they want to use native JSON primitives for such a scenario.

  • Anonymous
    June 23, 2009
    I don't have IE8 installed so I don't know if this will actually work, (Might give you a permission or other error) but can't you fix it locally by replacing the reference to the stringify function entirely with a wrapper? Something like: function fixStringify(){  var _stringify = JSON.stringify;  JSON.stringify = function(o, f){    if (o === "") o = "";    return _stringify(o, f);  } } fixStringify(); If the replacement work, all subsequent calls to JSON.stringify should be transparently wrapped. (The fixStringify function is only there to create a private namespace for the old reference.)

  • Anonymous
    June 23, 2009
    @nitro2k01: Yes, such a wrapper will also work. This is similar to the first two workarounds suggested above (but can be applied globally). One small issue in the snippet you provided is that stringify has three parameters instead of two - value, replacer and space.

  • Anonymous
    June 23, 2009
    I'm sure the JScript team would prefer to ship the fix ASAP, but thanks all the same for publishing the issue to your blog and offering workarounds rather than ignoring or pretending it doesn't exist and leaving it for developers to discover.

  • Anonymous
    June 25, 2009
    Is there somewhere we can vote on the bug (like https://connect.microsoft.com/)?

  • Anonymous
    June 26, 2009
    @Refael: You can provide your feedback at https://connect.microsoft.com/IE/Feedback.

  • Anonymous
    November 12, 2009
    Wow, you guys must not have written a real app on IE8 before you shipped it. Encoding form data in JSON ... that's a pretty common scenario.

  • Anonymous
    November 15, 2009
    my temp solution is: try {  document.getElementById("PanelsAfter").value = JSON.stringify(testData, "", 1); displayAll();  }  catch (err) {  alert("Diverting to PageMtce: Internet Explorer 8 has a bug which prevents this action. We recommend using FireFox or IE6 or 7 - NOT IE8");                return "ERROR";            } PLEASE FIX BUG URGENTLY!

  • Anonymous
    November 17, 2009
    I have trouble assigning null to an input element in IE8. Example: var field = document.createElement("input"); field.value = null; The result is: field.value = "null" I do not use JSON directly so my question is: Is this problem the same as discussed above??? Im not quite sure, but I think it is...

  • Anonymous
    November 17, 2009
    hmm... regarding my comment before it should say: the result is: field.value is a string with "null"

  • Anonymous
    January 05, 2010
    I would do a small but significant change: if (JSON.stringify(document.createElement("input").value)!== "" ) { } Above is more robust, than comparing with "'null'". And yes, inside this I would do a simple wrapper of JSON.stringify(). Which is again simpler and might cover some more subtle bugs in this same area.

  • Anonymous
    January 05, 2010
    Actually ... if (JSON.stringify(document.createElement("input").value)!== "" ) {  var _stringify = JSON.stringify;  JSON.stringify = function(o, f, s){     return _stringify(o === "" ? "" : o , f, s);  } }

  • Anonymous
    April 25, 2010
    hmm... regarding my comment before it should say: the result is: field.value is a string with "null"

  • Anonymous
    September 07, 2010
    Can we correctly presume that this will be fixed in IE9?