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" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   2: <html xmlns="http://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 (15)

  1. H says:

    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..?

  2. GauravS says:

    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.

  3. nitro2k01 says:

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

  4. GauravS says:

    @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.

  5. 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.

  6. Refael says:

    Is there somewhere we can vote on the bug (like https://connect.microsoft.com/)?

  7. GauravS says:

    @Refael: You can provide your feedback at https://connect.microsoft.com/IE/Feedback.

  8. Jonah says:

    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.

  9. 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!

  10. Tobi says:

    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…

  11. Tobi says:

    hmm… regarding my comment before it should say:

    the result is:

    field.value is a string with "null"

  12. DBJ says:

    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.

  13. DBJ says:

    Actually …

    if (JSON.stringify(document.createElement("input").value)!== "" ) {

     var _stringify = JSON.stringify;

     JSON.stringify = function(o, f, s){

        return _stringify(o === "" ? "" : o , f, s);

     }

    }

  14. hmm… regarding my comment before it should say:

    the result is:

    field.value is a string with "null"

  15. steve says:

    Can we correctly presume that this will be fixed in IE9?