Analisar a sequência JSON em um protótipo de object específico em JavaScript

Eu sei como analisar uma String JSON e transformá-lo em um object JavaScript. Você pode usar JSON.parse() em navegadores modernos (e IE9 +).

Isso é ótimo, mas como posso pegar esse Objeto JavaScript e transformá-lo em um Objeto JavaScript específico (ou seja, com um determinado protótipo)?

Por exemplo, suponha que você tenha:

 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 

Novamente, não estou querendo saber como converter uma string JSON em um object JavaScript genérico. Eu quero saber como converter uma string JSON em um object “Foo”. Isto é, meu Objeto deve agora ter uma function ‘teste’ e propriedades ‘a’ e ‘b’.

ATUALIZAÇÃO Depois de fazer algumas pesquisas, pensei nisso …

 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); 

Isso funcionará?

ATUALIZAÇÃO Maio de 2017 : A maneira “moderna” de fazer isso é via Object.assign , mas essa function não está disponível no IE 11 ou em navegadores mais antigos do Android.

As respostas atuais contêm muito código enrolado à mão ou biblioteca. Isso não é necessário.

  1. Use JSON.parse('{"a":1}') para criar um object simples.

  2. Use uma das funções padronizadas para definir o protótipo:

    • Object.assign(new Foo, { a: 1 })
    • Object.setPrototypeOf({ a: 1 }, Foo.prototype)

Veja um exemplo abaixo (este exemplo usa o object JSON nativo). Minhas alterações são comentadas no 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 

Você deseja adicionar a funcionalidade de serialização / desserialização do JSON, certo? Então olhe para isto:

Você quer conseguir isso:

UML

toJson () é um método normal.
fromJson () é um método estático.

Implementação :

 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); }; 

Uso :

 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 

Nota: Se você quiser, pode alterar todas as definições de propriedade como this.title , this.author , etc por var title , var author , etc. e adicionar getters a elas para realizar a definição UML.

Uma postagem do blog que achei útil: Entendendo os Protótipos do JavaScript

Você pode mexer com a propriedade __proto__ do object.

 var fooJSON = jQuery.parseJSON({"a":4, "b": 3}); fooJSON.__proto__ = Foo.prototype; 

Isso permite que fooJSON herde o protótipo Foo.

Eu não acho que isso funciona no IE, embora … pelo menos do que eu li.

Estou faltando alguma coisa na pergunta ou por que ninguém mencionou o parâmetro reviver do JSON.parse desde 2011?

Aqui está um código simplista para solução que funciona: 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: Sua pergunta é confusa: >> Isso é ótimo, mas como posso pegar esse Objeto JavaScript e transformá-lo em um Objeto JavaScript específico (ou seja, com um determinado protótipo)? contradiz o título, onde você pergunta sobre a análise de JSON, mas o parágrafo citado pergunta sobre a substituição de protótipo de object de tempo de execução de JS.

Por uma questão de perfeição, aqui está um simples one-liner com o qual acabei (não precisei verificar propriedades não-Foo):

 var Foo = function(){ this.bar = 1; }; // angular version var foo = angular.extend(new Foo(), angular.fromJson('{ "bar" : 2 }')); // jquery version var foo = jQuery.extend(new Foo(), jQuery.parseJSON('{ "bar" : 3 }')); 

Eu criei um pacote chamado json-dry . Suporta referências (circulares) e também instâncias de classs.

Você tem que definir 2 novos methods em sua class ( toDry no protótipo e unDry como um método estático), registre a class ( Dry.registerClass ), e lá vai você.

Uma abordagem alternativa poderia estar usando Object.create . Como primeiro argumento, você passa o protótipo e, para o segundo, passa um mapa de nomes de propriedades para descritores:

 function SomeConstructor() { }; SomeConstructor.prototype = { doStuff: function() { console.log("Some stuff"); } }; var jsonText = '{ "text": "hello wrold" }'; var deserialized = JSON.parse(jsonText); // This will build a property to descriptor map // required for #2 argument of Object.create var descriptors = Object.keys(deserialized) .reduce(function(result, property) { result[property] = Object.getOwnPropertyDescriptor(deserialized, property); }, {}); var obj = Object.create(SomeConstructor.prototype, descriptors); 

Embora isso não seja tecnicamente o que você deseja, se você souber de antemão o tipo de object que deseja manipular, poderá usar os methods call / apply do protótipo do seu object conhecido.

você pode mudar isso

 alert(fooJSON.test() ); //Prints 12 

para isso

 alert(Foo.prototype.test.call(fooJSON); //Prints 12 

Combinei as soluções que consegui encontrar e as compilei em uma solução genérica que pode analisar automaticamente um object personalizado e todos os seus campos de forma recursiva, para que você possa usar methods de protótipo após a desserialização.

Uma suposição é que você definiu um campo especial que indica seu tipo em cada object em que você deseja aplicar seu tipo automaticamente ( this.__type no exemplo).

 function Msg(data) { //... your init code this.data = data //can be another object or an array of objects of custom types. //If those objects defines `this.__type', their types will be assigned automatically as well this.__type = "Msg"; // < - store the object's type to assign it automatically } Msg.prototype = { createErrorMsg: function(errorMsg){ return new Msg(0, null, errorMsg) }, isSuccess: function(){ return this.errorMsg == null; } } 

uso:

 var responseMsg = //json string of Msg object received; responseMsg = assignType(responseMsg); if(responseMsg.isSuccess()){ // isSuccess() is now available //furhter logic //... } 

Função de atribuição de tipo (funciona recursivamente para atribuir tipos a quaisquer objects nesteds; também percorre as matrizes para encontrar qualquer object adequado):

 function assignType(object){ if(object && typeof(object) === 'object' && window[object.__type]) { object = assignTypeRecursion(object.__type, object); } return object; } function assignTypeRecursion(type, object){ for (var key in object) { if (object.hasOwnProperty(key)) { var obj = object[key]; if(Array.isArray(obj)){ for(var i = 0; i < obj.length; ++i){ var arrItem = obj[i]; if(arrItem && typeof(arrItem) === 'object' && window[arrItem.__type]) { obj[i] = assignTypeRecursion(arrItem.__type, arrItem); } } } else if(obj && typeof(obj) === 'object' && window[obj.__type]) { object[key] = assignTypeRecursion(obj.__type, obj); } } } return Object.assign(new window[type](), object); } 

Eu gosto de adicionar um argumento opcional para o construtor e chamar Object.assign(this, obj) , em seguida, manipular as propriedades que são objects ou matrizes de objects próprios:

 constructor(obj) { if (obj != null) { Object.assign(this, obj); if (this.ingredients != null) { this.ingredients = this.ingredients.map(x => new Ingredient(x)); } } } 

As respostas de Olivers são muito claras, mas se você está procurando uma solução em js angular, eu escrevi um bom módulo chamado Angular-jsClass que faz isso com facilidade, ter objects definidos em notação litaral é sempre ruim quando você está apontando para um grande projeto mas dizendo que os desenvolvedores enfrentam problema que exatamente o BMiner disse, como serializar um json para prototipar ou construir objects de notação

 var jone = new Student(); jone.populate(jsonString); // populate Student class with Json string console.log(jone.getName()); // Student Object is ready to use 

https://github.com/imalhasaranga/Angular-JSClass