I have an app that allows users to generate objects, and store them (in a MySQL table, as strings) for later use. The object could be :
function Obj() {
this.label = "new object";
}
Obj.prototype.setLabel = function(newLabel) {
this.label = newLabel;
}
If I use JSON.stringify on this object, I will only get the information on Obj.label (the stringified object would be a string like {label: "new object"}. If I store this string, and want to allow my user to retrieve the object later, the setLabel method will be lost.
So my question is: how can I re-instantiate the object, so that it keeps the properties stored thanks to JSON.stringify, but also gets back the different methods that should belong to its prototype. How would you do that ? I was thinking of something along “create a blank object” and “merge it with the stored one’s properties”, but I can’t get it to work.
To do this, you’ll want to use a “reviver” function when parsing the JSON string (and a “replacer” function or a
toJSONfunction on your constructor’s prototype when creating it). See Section 15.12.2 and 15.12.3 of the specification. If your environment doesn’t yet support native JSON parsing, you can use one of Crockford’s parsers (Crockford being the inventor of JSON), which also support “reviver” functions.Here’s a simple bespoke example that works with ES5-compliant browsers (or libraries that emulate ES5 behavior) (live copy, run in Chrome or Firefox or similar), but look after the example for a more generalized solution.
Note the
toJSONonFoo.prototype, and the function we pass intoJSON.parse.The problem there, though, is that the reviver is tightly coupled to the
Fooconstructor. Instead, you can adopt a generic framework in your code, where any constructor function can support afromJSON(or similar) function, and you can use just one generalized reviver.Here’s an example of a generalized reviver that looks for a
ctorproperty and adataproperty, and callsctor.fromJSONif found, passing in the full value it received (live example):To avoid having to repeat common logic in
toJSONandfromJSONfunctions, you could have generic versions:The advantage here being that you defer to the implementation of a specific “type” (for lack of a better term) for how it serializes and deserializes. So you might have a “type” that just uses the generics:
…or one that, for whatever reason, has to do something more custom:
And here’s how we might test that
FooandBarwork as expected (live copy):