How does moment.js know when it's object is being serialised? - javascript

From the moment.js docs
moment().toJSON(); When serializing an object to JSON, if there is a
Moment object, it will be represented as an ISO8601 string.
JSON.stringify({
postDate : moment()
}); // {"postDate":"2013-02-04T22:44:30.652Z"}
I don't understand how the moment object can detect the function operating on it. How is it able to return a different value when serialised, and when simply stored in an object, or returned as a string?

When using stringify, an object may define how it gets represented, as shown in this documentation:
From https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify
toJSON behavior
If an object being stringified has a property named toJSON whose
value is a function, then the toJSON method customizes JSON stringification
behavior: instead of the object being serialized, the value returned by the
toJSON method when called will be serialized.
For example:
var x = {
foo: 'foo',
toJSON: function () {
return 'bar';
}
};
var json = JSON.stringify({x: x});
//json will be the string '{"x":"bar"}'.
moment.js's documentation (seen here: https://raw.github.com/timrwood/moment/2.0.0/moment.js ) shows that this is indeed supported, here is the exact code
toJSON : function () {
return moment.utc(this).format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
}
So, that is how it is aware of how to represent itself when being stringified.

Related

Saving game in local storage results in [object Object] and undefined

model is an object that contains every single variable in my code in the form of arrays and objects. I want to save a snapshot of the values within whenever I use the saveGame function, and then redefine it to that snapshot whenever I use the loadGame function. However, when I use loadGame(), it returns [object Object], and then every single piece of information within becomes undefined. What gives?
function saveGame() {
localStorage.setItem('model', model);
}
function loadGame() {
model = localStorage.getItem('model');
updateView();
}
According to MDN web docs, keys and values stored in localStorage are always in UTF-16 DOMString format.
Therefore you can't store an object inside of a localStorage. Since it only accepts strings. The [object Object] you mentioned is a string representation of an object which it automatically creates using the default Object.toString() method.
To convert an object into a string representation and preserve the variables. You could serialize the object into a JSON string using JSON.stringify(). And later deserialize it using JSON.parse() to convert it back into an object.
function saveGame() {
localStorage.setItem('model', JSON.stringify(model));
}
function loadGame() {
model = JSON.parse(localStorage.getItem('model'));
updateView();
}

What does 'symbol-keyed' mean in `JSON.stringify`

There is a Object generated by Node.js, it looks like this when I use console.log:
{ dataValues: { a: 1, b: 2 }, fn1: function(){}, fn2: function(){} }
when I use JSON.stringify, it return this string:
{"a":1,"b":1}
I checked the mozilla developer center and found this:
All symbol-keyed properties will be completely ignored, even when using the replacer function.
I think the 'dataValues' must be the 'symbol-keyed' property, but what does 'symbol-keyed' mean?
btw, I use the sequelizejs ORM lib to generate this object.
I found the reason finally in the same page:
If an object being stringified has a property named toJSON whose value is a function, then the toJSON method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON method when called will be serialized.
It runs on browser normally.
Here is the jsfiddle to run it like I asked.
Code:
function test(data) {
for(var key in data){
this[key] = data[key];
}
}
test.prototype.toJSON = function(){
return this.dataValues;
}
var a = new test({dataValues: {a:1, b:2}});
console.log(a);//the instance
console.log(JSON.stringify(a));//{"a":1,"b":1}
Nah, the relevant part to your issue is this blurb:
If undefined, a function, or a symbol is encountered during conversion
it is either omitted (when it is found in an object) or censored to
null (when it is found in an array).
So in other words, if your JSON object contains functions, they are omitted during the JSON.stringify process.
"symbol-keyed" refers to a new primitive type introduced in ecmascript6. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof for more info.
This is an example of a "symbol-keyed" property:
{[Symbol("foo")]: "foo"}
Reference for JavaScript Symbol: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

JSON.stringify and non associative array

I want to serialize in json a non associative array and the output is quite disturbing
JSON.stringify([1]);
// Expected : "[1]"
Output : "\"[1]\""
It treats the array as a string, what am i missing ?
I'm using Chrome Version 29.0.1547.65
The issue you are seeing is because of an Array.prototype.toJSON method that has been defined incorrectly with respect to the semantics of JSON.stringify. See below:
From: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
toJSON behavior
If an object being stringified has a property named toJSON whose value
is a function, then the toJSON method customizes JSON stringification
behavior: instead of the object being serialized, the value returned
by the toJSON method when called will be serialized.
When an object has a toJSON method the result of that method will be stringified in its place. If the toJSON method is defined as a stringification then the object will be double stringified.
The only work-around I am aware of is to remove the method or to implement your own stringify() method with different semantics than the built-in.
If you can, simply remove the method from Array.prototype. If you are concerned this will break other functionality on the page then you need to remove it, stringify, then restore it.
function myStringify( o ) {
var temp = Array.prototype.toJSON;
delete Array.prototype.toJSON;
var result = JSON.stringify(o);
Array.prototype.toJSON = temp;
return result;
}

"too much recursion" error when calling JSON.stringify on a large object with circular dependencies

I have an object that contains circular references, and I would like to look at the JSON representation of it. For example, if I build this object:
var myObject = {member:{}};
myObject.member.child = {};
myObject.member.child.parent = myObject.member;
and try to call
JSON.stringify(myObject);
I get the error "too much recursion", not surprisingly. The "child" object has a reference to its "parent" and the parent has a reference to its child. The JSON representation does not have to be perfectly accurate, as I'm only using it for debugging, not for sending data to a server or serializing the object into a file or anything like that. Is there a way to tell JSON.stringify to just ignore certain properties (in this case the parent property of the child object), so that I would get:
{
"member" : {
"child" : {}
}
}
The closest I can think of is to use getChild() and getParent() methods instead of just members, because JSON.stringify ignores any properties that are functions, but I'd rather not do that if I don't have to.
You can pass a function as the second argument to stringify.
This function receives as arguments the key and value of the member to stringify.
If this function returns undefined, the member will be ignored.
alert(JSON.stringify(myObject, function(k, v) {
return (k === 'member') ? undefined : v;
}));
...or use e.g. firebug or use the toSource()-method, if you only want to see whats inside the object.
alert(myObject.toSource());
From the crockford implementation (which follows the ECMA specification):
If the stringify method sees an object that contains a toJSON method, it calls that method, and stringifies the value returned. This allows an object to determine its own JSON representation.
Then something like this should work just fine:
var myObject =
{
member: { child: {} }
}
myObject.member.child.parent = myObject.member;
myObject.member.child.toJSON = function ()
{
return 'no more recursion for you.';
};
console.log(JSON.stringify(myObject));​
http://jsfiddle.net/feUtk/

How to use JSON to re-build the Javascript Object?

I have an object like this:
var someObj = Class.create ({
initialize: function(objName){
this.objName = objName;
}
});
I can use
o = new someObj("objName");
to make an obj. I can use Object.toJSON(o) to change the o to become a JSON String,
but I want the JSON String convert back to someObj, so, I use eval() to pass the
JSON String to become an object, but the question is, it can become a JS Obj,
but the constructor of "o" is not someObj. How can I eval the JSON String by using
"someObj" as the constructor?
JSON strings cannot represent objects with member functions, so the only thing you will get out of a JSON string is raw data. Assuming the toJSON method results in a JSON string representing an object with all the non-function members of your class instance, you should be able to take the resulting object and attach the prototype to get all the functions back. For example, using jQuery's handy extend function:
var o = new someObj("objName");
var json = Object.toJSON(o);
var json_obj = eval(json);
$.extend(json_obj, someObj.prototype);
json_obj.someMethodDefinedOnsomeObj()
Depending on how the framework you are using to represent classes in JavaScript makes use of the prototypal object model, your milage may very with the above example. Also, using eval() creates a security hole, so if you do not trust where the JSON string is coming from, you should use a different de-serialization method. Just for full coverage, here is how I did it with raw prototypes:
function Animal(name){
this.name = name;
}
Animal.prototype.talk = function(){
console.log("My name is "+this.name);
}
var a = new Animal("Brendan Eich");
a.talk();
var json = '{name: "Tim Berners-Lee"}'
var b = eval(b);
$.extend(b, Animal.prototype);
b.talk();
In a firebug console this produces the output:
My name is Brendan Eich
My name is Tim Berners-Lee
See JSON revivers at http://json.org/js.html
var myObject = JSON.parse(myJSONtext, reviver);
The optional reviver parameter is a
function that will be called for every
key and value at every level of the
final result. Each value will be
replaced by the result of the reviver
function. This can be used to reform
generic objects into instances of
pseudoclasses, or to transform date
strings into Date objects.
you're using prototypejs right? i've always found that tricky and ended up just making my own initializer that read in an object that had been evaled or the json string itself.

Categories