From 2ed81708bdacfd4b97b77baef67ad8b75205dd20 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Mon, 14 Feb 2011 16:22:23 -0600 Subject: [PATCH] Hide metadata when serializing JS objects using JSON.stringify via a toJSON hack. Fixes #8108. --- src/data.js | 26 +++++++++++++++++++++++--- test/unit/data.js | 32 +++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/data.js b/src/data.js index 9fee459..66d2292 100644 --- a/src/data.js +++ b/src/data.js @@ -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 ); diff --git a/test/unit/data.js b/test/unit/data.js index 455b923..c6ef843 100644 --- a/test/unit/data.js +++ b/test/unit/data.js @@ -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 -- 1.7.10.4