Разбирать строку JSON в специальный прототип объекта в JavaScript
Я знаю, как разбирать строку JSON и превращать ее в объект JavaScript.
Вы можете использовать JSON.parse()
в современных браузерах (и IE9 +).
Это здорово, но как я могу взять этот объект JavaScript и превратить его в конкретный объект JavaScript (т.е. с определенным прототипом)?
Например, предположим, что у вас есть:
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
Опять же, мне не интересно, как преобразовать строку JSON в общий объект JavaScript. Я хочу знать, как преобразовать строку JSON в объект "Foo". То есть, у моего объекта теперь должна быть функция "test" и свойства "a" и "b".
UPDATE
После некоторых исследований я подумал об этом...
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);
Будет ли это работать?
ОБНОВЛЕНИЕ Май 2017 г.. "Современный" способ сделать это через Object.assign
, но эта функция недоступна в браузерах IE 11 или старше.
Ответы
Ответ 1
В текущих ответах содержится много ручного или библиотечного кода. Это необязательно.
Ответ 2
См. пример ниже (в этом примере используется собственный объект JSON). Мои изменения прокомментированы в 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
Ответ 3
Вы хотите добавить функциональность сериализации/десериализации JSON, правильно? Затем посмотрите на это:
Вы хотите достичь этого:
![UML]()
toJson() - нормальный метод.
fromJson() является статическим методом.
Реализация
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);
};
Использование
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
Примечание. Если вы хотите, вы можете изменить все определения свойств, такие как this.title
, this.author
и т.д., var title
, var author
и т.д. и добавить к ним геттеры для выполнения определения UML.
Ответ 4
Сообщение в блоге, которое я нашел полезным:
Общие сведения о прототипах JavaScript
Вы можете использовать свойство __proto__ объекта.
var fooJSON = jQuery.parseJSON({"a":4, "b": 3});
fooJSON.__proto__ = Foo.prototype;
Это позволяет fooJSON наследовать прототип Foo.
Я не думаю, что это работает в IE, хотя... по крайней мере, из того, что я прочитал.
Ответ 5
Для полноты, здесь простой однострочный я закончил с (я не нуждался в проверке не-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 }'));
Ответ 6
Альтернативный подход может использовать Object.create
. В качестве первого аргумента вы передаете прототип, а для второго вы передаете карту имен свойств в дескрипторы:
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);
Ответ 7
Мне что-то не хватает в этом вопросе или почему еще никто не упоминал reviver
параметр JSON.parse
с 2011 года?
Вот упрощенный код для решения, который работает:
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: Ваш вопрос путается: → Это здорово, но как я могу взять этот объект JavaScript и превратить его в конкретный объект JavaScript (т.е. с определенным прототипом)?
противоречит заголовку, в котором вы спрашиваете о разборе JSON, но цитируемый параграф спрашивает о замене прототипа объекта JS.
Ответ 8
Пока это технически не то, что вы хотите, если вы знаете, что тип объекта, с которым вы хотите обращаться, может использовать методы вызова/применения прототипа вашего известного объекта.
вы можете изменить это
alert(fooJSON.test() ); //Prints 12
к этому
alert(Foo.prototype.test.call(fooJSON); //Prints 12
Ответ 9
Я объединил решения, которые я смог найти и скомпилировал в общий, который может автоматически анализировать пользовательский объект и все его рекурсивно, чтобы вы могли использовать методы прототипа после десериализации.
Одно из предположений состоит в том, что вы определили специальную подачу, которая указывает, что она вводится в каждом объекте, который вы хотите применить автоматически, (this.__type
в примере).
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 type to assign it automatically
}
Msg.prototype = {
createErrorMsg: function(errorMsg){
return new Msg(0, null, errorMsg)
},
isSuccess: function(){
return this.errorMsg == null;
}
}
использование:
var responseMsg = //json string of Msg object received;
responseMsg = assignType(responseMsg);
if(responseMsg.isSuccess()){ // isSuccess() is now available
//furhter logic
//...
}
Функция назначения типа (она рекурсивно работает, чтобы назначать типы для любых вложенных объектов, а также итерации через массивы для поиска любых подходящих объектов):
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);
}
Ответ 10
Ответы Olivers очень ясны, но если вы ищете решение в angular js, я написал хороший модуль под названием Angular -jsClass, который делает это легко, поскольку объекты, определенные в литальной нотации, всегда плохие, когда вы стремитесь к большому проекту, но заявляете, что разработчики сталкиваются с проблемой, о которой точно говорит BMiner, о том, как сериализовать jon для прототипов или объектов нотации конструктора.
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