Hide metadata when serializing JS objects using JSON.stringify via a toJSON hack...
authorColin Snover <github.com@zetafleet.com>
Mon, 14 Feb 2011 22:22:23 +0000 (16:22 -0600)
committerColin Snover <github.com@zetafleet.com>
Mon, 14 Feb 2011 22:22:23 +0000 (16:22 -0600)
src/data.js
test/unit/data.js

index 9fee459..66d2292 100644 (file)
@@ -24,7 +24,7 @@ jQuery.extend({
        hasData: function( elem ) {
                elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
 
-               return !!elem && !jQuery.isEmptyObject(elem);
+               return !!elem && !isEmptyDataObject( elem );
        },
 
        data: function( elem, name, data, pvt /* Internal Use Only */ ) {
@@ -64,6 +64,13 @@ jQuery.extend({
 
                if ( !cache[ id ] ) {
                        cache[ id ] = {};
+
+                       // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
+                       // metadata on plain JS objects when the object is serialized using
+                       // JSON.stringify
+                       cache[ id ].toJSON = function () {
+                               return undefined;
+                       };
                }
 
                // An object can be passed to jQuery.data instead of a key/value pair; this gets
@@ -130,7 +137,7 @@ jQuery.extend({
 
                                // If there is no data left in the cache, we want to continue
                                // and let the cache object itself get destroyed
-                               if ( !jQuery.isEmptyObject(thisCache) ) {
+                               if ( !isEmptyDataObject(thisCache) ) {
                                        return;
                                }
                        }
@@ -142,7 +149,7 @@ jQuery.extend({
 
                        // Don't destroy the parent cache unless the internal data object
                        // had been the only thing left in it
-                       if ( !jQuery.isEmptyObject(cache[ id ]) ) {
+                       if ( !isEmptyDataObject(cache[ id ]) ) {
                                return;
                        }
                }
@@ -291,4 +298,17 @@ function dataAttr( elem, key, data ) {
        return data;
 }
 
+// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON
+// property to be considered empty objects; this property always exists in
+// order to make sure JSON.stringify does not expose internal metadata
+function isEmptyDataObject( obj ) {
+       for ( var name in obj ) {
+               if ( name !== "toJSON" ) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 })( jQuery );
index 455b923..c6ef843 100644 (file)
@@ -180,7 +180,13 @@ test(".data()", function() {
        var div = jQuery("#foo");
        strictEqual( div.data("foo"), undefined, "Make sure that missing result is undefined" );
        div.data("test", "success");
-       same( div.data(), {test: "success"}, "data() get the entire data object" );
+
+       var dataObj = div.data();
+
+       // TODO: Remove this hack which was introduced in 1.5.1
+       delete dataObj.toJSON;
+
+       same( dataObj, {test: "success"}, "data() get the entire data object" );
        strictEqual( div.data("foo"), undefined, "Make sure that missing result is still undefined" );
 
        var nodiv = jQuery("#unfound");
@@ -189,7 +195,10 @@ test(".data()", function() {
        var obj = { foo: "bar" };
        jQuery(obj).data("foo", "baz");
 
-       var dataObj = jQuery.extend(true, {}, jQuery(obj).data());
+       dataObj = jQuery.extend(true, {}, jQuery(obj).data());
+
+       // TODO: Remove this hack which was introduced for 1.5.1
+       delete dataObj.toJSON;
 
        deepEqual( dataObj, { foo: "baz" }, "Retrieve data object from a wrapped JS object (#7524)" );
 });
@@ -329,12 +338,18 @@ test("data-* attributes", function() {
                num++;
        }
 
+       // TODO: Remove this hack which was introduced for 1.5.1
+       num--;
+
        equals( num, check.length, "Make sure that the right number of properties came through." );
 
        for ( var prop in obj2 ) {
                num2++;
        }
 
+       // TODO: Remove this hack which was introduced for 1.5.1
+       num2--;
+
        equals( num2, check.length, "Make sure that the right number of properties came through." );
 
        child.attr("data-other", "newvalue");
@@ -465,4 +480,15 @@ test(".removeData()", function() {
 
        div.removeData("test.foo");
        equals( div.data("test.foo"), undefined, "Make sure data is intact" );
-});
\ No newline at end of file
+});
+
+if (window.JSON && window.JSON.stringify) {
+       test("JSON serialization (#8108)", function () {
+               expect(1);
+
+               var obj = { foo: "bar" };
+               jQuery.data(obj, "hidden", true);
+
+               equals( JSON.stringify(obj), '{"foo":"bar"}', "Expando is hidden from JSON.stringify" );
+       });
+}
\ No newline at end of file