Parse JSON String into a Particular Object Prototype in JavaScript
Solution 1
The current answers contain a lot of hand-rolled or library code. This is not necessary.
Use
JSON.parse('{"a":1}')
to create a plain object.Use one of the standardized functions to set the prototype:
Object.assign(new Foo, { a: 1 })
Object.setPrototypeOf({ a: 1 }, Foo.prototype)
Solution 2
See an example below (this example uses the native JSON object). My changes are commented in CAPITALS:
function Foo(obj) // CONSTRUCTOR CAN BE OVERLOADED WITH AN OBJECT
{
this.a = 3;
this.b = 2;
this.test = function() {return this.a*this.b;};
// IF AN OBJECT WAS PASSED THEN INITIALISE PROPERTIES FROM THAT OBJECT
for (var prop in obj) this[prop] = obj[prop];
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
// INITIALISE A NEW FOO AND PASS THE PARSED JSON OBJECT TO IT
var fooJSON = new Foo(JSON.parse('{"a":4,"b":3}'));
alert(fooJSON.test() ); //Prints 12
Solution 3
Do you want to add JSON serialization/deserialization functionality, right? Then look at this:
You want to achieve this:
toJson() is a normal method.
fromJson() is a static method.
Implementation:
var Book = function (title, author, isbn, price, stock){
this.title = title;
this.author = author;
this.isbn = isbn;
this.price = price;
this.stock = stock;
this.toJson = function (){
return ("{" +
"\"title\":\"" + this.title + "\"," +
"\"author\":\"" + this.author + "\"," +
"\"isbn\":\"" + this.isbn + "\"," +
"\"price\":" + this.price + "," +
"\"stock\":" + this.stock +
"}");
};
};
Book.fromJson = function (json){
var obj = JSON.parse (json);
return new Book (obj.title, obj.author, obj.isbn, obj.price, obj.stock);
};
Usage:
var book = new Book ("t", "a", "i", 10, 10);
var json = book.toJson ();
alert (json); //prints: {"title":"t","author":"a","isbn":"i","price":10,"stock":10}
var book = Book.fromJson (json);
alert (book.title); //prints: t
Note: If you want you can change all property definitions like this.title
, this.author
, etc by var title
, var author
, etc. and add getters to them to accomplish the UML definition.
Solution 4
A blog post that I found useful: Understanding JavaScript Prototypes
You can mess with the __proto__ property of the Object.
var fooJSON = jQuery.parseJSON({"a":4, "b": 3});
fooJSON.__proto__ = Foo.prototype;
This allows fooJSON to inherit the Foo prototype.
I don't think this works in IE, though... at least from what I've read.
Solution 5
Am I missing something in the question or why else nobody mentioned reviver
parameter of JSON.parse
since 2011?
Here is simplistic code for solution that works: https://jsfiddle.net/Ldr2utrr/
function Foo()
{
this.a = 3;
this.b = 2;
this.test = function() {return this.a*this.b;};
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse(`{"a":4, "b": 3}`, function(key,value){
if(key!=="") return value; //logic of course should be more complex for handling nested objects etc.
let res = new Foo();
res.a = value.a;
res.b = value.b;
return res;
});
// Here you already get Foo object back
alert(fooJSON.test() ); //Prints 12
PS: Your question is confusing: >>That's great, but how can I take that JavaScript Object and turn it into a particular JavaScript Object (i.e. with a certain prototype)? contradicts to the title, where you ask about JSON parsing, but the quoted paragraph asks about JS runtime object prototype replacement.
Related videos on Youtube
BMiner
Updated on July 08, 2022Comments
-
BMiner almost 2 years
I know how to parse a JSON String and turn it into a JavaScript Object. You can use
JSON.parse()
in modern browsers (and IE9+).That's great, but how can I take that JavaScript Object and turn it into a particular JavaScript Object (i.e. with a certain prototype)?
For example, suppose you have:
function Foo() { this.a = 3; this.b = 2; this.test = function() {return this.a*this.b;}; } var fooObj = new Foo(); alert(fooObj.test() ); //Prints 6 var fooJSON = JSON.parse({"a":4, "b": 3}); //Something to convert fooJSON into a Foo Object //....... (this is what I am missing) alert(fooJSON.test() ); //Prints 12
Again, I am not wondering how to convert a JSON string into a generic JavaScript Object. I want to know how to convert a JSON string into a "Foo" Object. That is, my Object should now have a function 'test' and properties 'a' and 'b'.
UPDATE After doing some research, I thought of this...
Object.cast = function cast(rawObj, constructor) { var obj = new constructor(); for(var i in rawObj) obj[i] = rawObj[i]; return obj; } var fooJSON = Object.cast({"a":4, "b": 3}, Foo);
Will that work?
UPDATE May, 2017: The "modern" way of doing this, is via
Object.assign
, but this function is not available in IE 11 or older Android browsers.-
Bergi over 8 years
-
-
BMiner about 13 yearsI suppose you could do the "opposite" of this, as well. Construct a blank Foo Object and copy the properties from fooJSON into the new Foo Object. Finally, set fooJSON to point to the Foo Object.
-
Gabriel Llamas about 13 yearsThis is very dangerous. If the obj has an attribute that is not in Foo definition, you will create a Foo object with an extra hidden property that you don't know its name... Instead of a loop I will simply do: this.a = obj.a and this.b = obj.b. Or directly I would pass "a" and "b" as parameters: new Foo (obj.a, obj.b)
-
BMiner about 13 years@GagleKas I wouldn't say dangerous. Hidden properties would be OK as long as you are aware of their existence. I am just trying to implement basic Object deserialization of a JSON Object.
-
Oliver Moran about 13 yearsGagleKas's advice is worth listening to. (Although "very dangerous" is a little OTT.) The example is above is just to give you an idea. The correct implementation will depend on your application.
-
Oliver Moran about 13 yearsActually, something like that was my first instinct.
-
Gabriel Llamas about 13 yearsWell, instead of "very dangerous" you can put "is not recommended". If you want to add serialization/deserialization to an object look at my answer.
-
BMiner about 13 yearsI agree. This implementation will definitely work, and it's great... just a little wordy and specific to the Book Object. IMHO, the power of JS comes from prototypes and the ability to have some extra properties if you want to. That's all I'm sayin'. I was really looking for the one-liner: x.__proto__ = X.prototype; (although it's not IE browser compatible at this time)
-
nnnnnn about 13 yearsDon't forget that your
toJson()
method - regardless of whether it has individual properties hardcoded or uses a for each - will need to add backslash escape codes for some characters that could be in each string property. (A book title might have quotation marks, for example.) -
Gabriel Llamas about 13 yearsYes, I know, my answer was an example and the best answer for the question, but... not even a positive point... I don't know why I waste my time helping others
-
BMiner about 13 yearsI really appreciated your response and opinion, so you get the +1. It was definitely helpful; unfortunately, it isn't quite the best answer. Thanks!
-
Darbio over 12 yearsThis solved my problem of trying to type cast from a JSON string to a type, allowing me to use the functions on my custom Type.
-
Yu Asakusa about 10 yearsNote that
__proto__
has long been deprecated. Moreover, for performance reasons, it is not recommended to modify the [[Prototype]] internal property of an already created object (by setting__proto__
or by any other means). -
Romain Vergnory over 9 yearsYou might want to protect yourself from prototype properties.
for (var prop in obj) {if (obj.hasOwnProperty(prop)) {this[prop] = obj[prop];}}
-
stone about 8 yearsThese days I would use
JSON.stringify()
instead of writing toJSon() myself. No need to reinvent the wheel now that all modern browsers support it. -
Wim Leers about 8 yearsAlas, none of the actually non-deprecated solutions are far more complex than this…
-
Atticus almost 8 yearsAgreed with @skypecakes. If you want to only serialize a subset of properties, create a constant of serializable properties.
serializable = ['title', 'author', ...]
.JSON.stringify(serializable.reduce((obj, prop) => {...obj, [prop]: this[prop]}, {}))
-
tekHedd over 7 years@RomainVergnory For even more safety, I only initialize properties created in the constructor, this instead of obj:
for (var prop in obj) {if (this.hasOwnProperty(prop)) {this[prop] = obj[prop];}}
. This assumes you expect the server to populate all properties, IMO should also throw if obj.hasOwnProperty() fails... -
Bogdan Mart over 7 yearsI've made some tests of performance of changing
[[prototype]]
and it's seems to be irrelevant in Chrome. In firefox calling new is slower than using prototype, and Object.create is fastest. I guess issue with FF is that first test is slower than last, just order of execution matters. In chrome everything runs with almost same speed. I mean property access and nvocation of methods. Creatin is faster with new, but that's not so important. see: jsperf.com/prototype-change-test-8874874/1 and: jsperf.com/prototype-changed-method-call -
stakx - no longer contributing about 7 yearsI suppose these days, one would call
Object.setPrototypeOf(fooJSON, Foo.prototype)
instead of settingfooJSON.__proto__
... right? -
BMiner about 7 yearsObject.assign is not available in older browsers including IE and older Android browsers. kangax.github.io/compat-table/es6/…
-
christo8989 almost 7 yearsThere's also a big warning against using
Object.setPrototypeOf(...)
. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… -
Erik van Velzen over 6 years@SimonEpskamp That code does not work. Check your url, the second parameter to
setPrototypeOf
are property descriptors. -
Vojta over 6 yearsSolution with setting prototype doesn't work if there is some property which also needs to have prototype. In other words: it only solves first level of data hierarchy.
-
vir us over 6 yearscheck out my solution below that applies Object.assign(..) recursively that can automatically resolve properties (with a bit of information provided in advance)
-
Erik Philips almost 5 yearsIf you want to serialize a subset, just implement
toJSON()
and return an anonymous object you what to serialize asJSON.stringify()
will use toJSON() automatically. -
baHI over 4 yearsWhile I worked this up due it's simplicity, there's a more performant, generic solution on the bottom, with my single vote. It deserves also votes plus that solution has no warning: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
-
Marcel over 2 yearsObject.assign does work, but as others said, it is not recursively.