Allow Objects and Arrays to be stored as JSON #285
Conversation
Only works on actual {} Objects (and [] Arrays), not typeof "object". Tested with v.constructor === Object || v.constructor === Array Detects JSON stored in cookies by looking for '[' or '{', using the same RegEx as jQuery's data() function. If the JSON can't be parsed it returns a string, to allow strings that have brackets like JSON but aren't, e.g. "[hello, this isn't JSON]" Doesn't interfere with existing config.json, which can still be used to force JSON parsing. Added 6x tests for reading, saving, and raw URL encoded JSON. Updated README.md to document new behaviour.
I think removing |
I liked the idea of releasing a minor version with all the latest changes, and then a 1.5 beta including this change. Just thinking, isn't removing json config doing any harm at all? Anyone not using it will get automatic json parsing for free, anyone having it turned on will still get JSON parsed out, no? |
Unless I'm overlooking something the only thing that isn't backwards compatible here is when someone is storing values other than Array or Object with json config active, like booleans. See my comment here: #286 (comment) |
Another thing I'm thinking about is whether it's worth it to move all of the parsing logic out of the plugin, and instead utilize |
Whether The one thing I've noted is: if |
@carhartl Now that I'm re-reading it, they do testing for |
Exactly. Imo reading from a cookie should behave exactly like E.g. as if the following would work: jQuery.data(document.cookie, 'fookey'); |
Yes, I was refering to some details of the I like the idea of exposing But there is also the version compatibility issue, older versions of jQuery could not have |
I am no longer sure if we should go for pushing a new jQuery API method to use, as this would make the plugin require the latest version, which seems needlessly restrictive? |
Not only require latest version but depend on external support (I am pretty sure jQuery team will not create a new API for this reason :D) Looks like the only feasible solution here is code duplication (as it is already being done). |
Not sure about the duplication. I'm trying a bit tonight to see whether we can use |
I really wouldn't want to duplicate the entire type parsing logic. |
You'd only be using similar logic for reading cookies. It's not huge either, just a couple of lines — you already had most of it for the There is no logic for writing in I'd guess the reason jQuery don't expose it is because they figured it's such a tiny bit of logic, no-one would ever mind duplicating it. I think after you've removed the |
Wasn't it also pulling true/false values back out? Maybe numbers? To me this isn't even so much about the duplication. I see the benefits in reducing friction and future maintenance effort. If we use data(), reading a cookie value will always behave the same, per jQuery version that is. And if at some point the method is enhanced to do something new, we/users of the plugin get that for free, without us having to adapt anything at all. All that being said, I'm not even sure whether it can work like that, to me that would just be the ideal I'd like to give a shot at. |
In this case I think the ideia of using a detached DOM still stands (if it actually works)... |
I can see the benefit of that, I just can't see many situations where you'd want jquery-cookie to convert types other than basic native types, Their full code is this: function dataAttr( elem, key, data ) {
var name;
// If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if ( data === undefined && elem.nodeType === 1 ) {
name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
data = elem.getAttribute( name );
if ( typeof data === "string" ) {
try {
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
// Only convert to a number if it doesn't change the string
+data + "" === data ? +data :
rbrace.test( data ) ? jQuery.parseJSON( data ) :
data;
} catch( e ) {}
// Make sure we set the data so it isn't changed later
data_user.set( elem, key, data );
} else {
data = undefined;
}
}
return data;
} So to fake it, you'd need a data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
+data + "" === data ? +data :
rbrace.test( data ) ? jQuery.parseJSON( data ) :
data; Is pretty neat and self-contained. I don't really see that code looking much different unless Javascript itself changes significantly. |
I think I soon will be convinced :) |
@carhartl I think you will, because data attribute is a specification (internal implementations doesn't matter and aren't suppose to change). Just document saying that the process is similar to the spec and you are good to go :D |
Interestingly: var something = {}
$.data(something, 'foo', 42)
$.data(something, 'foo') // => 42 |
No, not interesting, because |
Nonetheless, here's a proof-of-concept: 65a9d24 It required only a tiny change (two lines) without any duplication of code from anywhere at all. Downside (although that'd be the same with the given PR): The plugin would require To speed up things that |
I guess that's pretty solid — it's just a bit weird :D If you're caching the var $div = $('<div>');
$div.attr('data-cookie', '[1, 2]').data('cookie'); // [1, 2]
$div.attr('data-cookie', '[1, 2, 3]').data('cookie'); // STILL [1, 2] I don't think there's too much overhead instantiating each time. People really aren't going to call thousands of cookies — the expensive part is actually attaching to the DOM. On balance though, because of the weirdness of this, I'd probably still vote for just duplicating the logic. The size saving isn't worth the potential for this breakage if jQuery decide to change something in |
Planning to release a version 2.0 with this, along with #295. |
Sounds great :D |
@@ -29,7 +30,12 @@ | |||
} | |||
|
|||
function stringifyCookieValue(value) { | |||
return encode(config.json ? JSON.stringify(value) : String(value)); | |||
// If the value is a real Object or Array, stringify it with JSON.stringify | |||
if (value.constructor === Array || value.constructor === Object) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not safe ways of identifying arrays or objects (neither works with foreign objects, and the Object case doesn't account for non-plain objects, which JSON.stringify does). I'd recommend using value !== null && typeof value === 'object'
which covers plain objects, arrays and any other type of object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought, you may want to take the approach of data serialisation as is done for memcached/redis systems. Which is to use stringify()
for all values that are not integer or boolean (even plain strings). This due to the inherent ambiguity with free-form strings once stored.
Hi @dhoulb, just so you know JSON handling is being developed in branch 2.0. Here are the initial docs of the feature. This is still in development, so any suggestion is welcome before landing in master. Closing this since it was fixed in commit js-cookie/js-cookie@a52398d |
Continued from #283
Messed up the previous pull request, so I've made a fresh clean one. This one also removes the
json
configuration directive, in favour of the automatic JSON object and array parsing.README updated to reflect the new behaviour. Tests updated, and additional tests added to test new functionality.