Отображение объектов JSON для объектов Javascript
При использовании AJAX я, как правило, передаю объекты с моего сервера на Javascript в виде объектов JSON (он же Javascript). Некоторые функции в моем Javascript зависят от конкретного типа объекта, который я использую. Например, можно использовать номер телефона, например. У меня есть конструктор:
function PhoneNumber(number, type, isPrimary, contactId, id, className) {
this.number = number;
this.type = type;
this.isPrimary = isPrimary;
this.contactId = contactId;
this.id = id;
this.className = className;
}
Что я использую при создании объекта номера телефона в своем Javascript. В некоторых ситуациях я не создаю объект в JS, я получаю объект с сервера, поэтому он поставляется в виде общего объекта с точно такими же полями. Поэтому, когда мой код полагается на конкретный тип, используя что-то вроде этого:
var objType = objArray[i].constructor.name;
var mappedObj;
switch(objType) {
case 'PhoneNumber':
currentArray = currentArray.phone;
//Convert response to javascript object.
mappedObj = mapPhone(jsonResponse[i]);
break;
case 'Email':
currentArray = currentArray.email;
mappedObj = mapEmail(jsonResponse[i]);
break;
case 'Address':
currentArray = currentArray.address;
mappedObj = mapAddress(jsonResponse[i]);
break;
case 'Website':
currentArray = currentArray.website;
mappedObj = mapWebsite(jsonResponse[i]);
}
В этой ситуации я проверяю имя конструктора объектов и задаю определенные переменные на основе этого имени. Если объект, на котором я проверяю имя, является JSON с сервера, он просто дает мне общий ответ "Объект", и, следовательно, код не работает. Я обойду это, используя функцию отображения для каждого объекта, например:
function mapPhone(phoneObj) {
var id = phoneObj.id;
var contactId = phoneObj.contactId;
var number = phoneObj.number;
var type = phoneObj.type;
var primary = phoneObj.isPrimary;
var className = phoneObj.className;
var phoneNumber = new PhoneNumber(number, type, primary, contactId, id, className);
return phoneNumber;
}
Это работает отлично, но мне кажется немного лишним. Это лучший способ решить проблему объекта JSON, или есть лучшее решение? Я понимаю, что это скорее вопрос "Я делаю это как можно лучше", но я повторяю этот тип логики ПОСТОЯННО в своем Javascript-коде, и я полагаю, что я мог бы получить еще одно мнение или два о том, правильный способ сделать это, прежде чем я буду тратить час на час, фиксируя его в будущем.
EDIT: я решил принять решение jQuery, потому что я использовал jQuery в своем проекте. Однако есть несколько решений, которые также работали для меня, прежде чем я нашел параметр jQuery. Они просто были не такими чистыми и эффективными.
Ответы
Ответ 1
Следующее требует, чтобы у вас были одинаковые свойства в объекте и вашем объекте JSON.
var phoneNumber = $.extend(new PhoneNumber(), yourJSONObject);
Это создает новый объект PhoneNumber, а затем копирует на него все свойства вашего объекта JSON. Метод $.extend()
относится к jQuery, но вы также можете использовать его в качестве аналогичного метода, например. Underscore.js или одна из других js-библиотек/фреймворков.
Ответ 2
В этом подобном вопросе есть много интересных ответов:
Разбирайте строку JSON в специальный прототип объекта в JavaScript
Основываясь на собственном ответе плаката, я думаю, что это было бы эффективным решением для вас:
function recastJSON(jsonObject) {
// return generic object if objectType is not specified
if (!jsonObject.objectType)
return jsonObject;
// otherwise create a new object of type specified
var obj = eval('new '+jsonObject.objectType+'()');
for(var i in jsonObject)
obj[i] = jsonObject[i];
return obj;
}
Вам нужно будет добавить objectType к объектам JSON, которые вы получаете, чтобы определить класс javascript, который вы хотите создать. Затем, когда вы вызываете эту функцию, она будет передавать объект этому типу и копировать данные (включая переменную "objectType" ).
Используя пример номера телефона, ваш код будет выглядеть так:
// generic object from JSON
var genericObject = {objectType:'PhoneNumber', number:'555-555-5555', type:'home', primary:true, contactId:123, id:1234, className:'stdPhone'};
var phoneObject = recastJSON(genericObject);
Ответ 3
AFAIK, во всем, что не является IE, вы можете сделать это:
// define a class
var Foo = function(name) {
this.name = name;
}
// make a method
Foo.prototype.shout = function() {
return "I am " + this.name;
}
// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// force its class to be Foo
x.__proto__ = Foo.prototype;
// the method works
x.shout();
К сожалению, IE не поддерживает аксессуар __proto__
, поэтому вам нужно будет сначала создать пустой экземпляр вашего объекта, а затем просто скопировать все:
// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// make an empty Foo
var y = Object.create(Foo.prototype);
// copy stuff over
for (a in x) {
y[a] = x[a];
}
y.shout();
Оба этих подхода довольно немного более общие, чем ваши функции mapWhatever
, сохраняя его DRY.
Ответ 4
Если поддержка старых браузеров не поддерживается, вы можете использовать Object.create
, чтобы сделать сопоставление для вас. (отбрасывание прокладки - по крайней мере, прокладка на MDN-in будет не фиксировать старые браузеры, поскольку эта прокладка не принимает второй параметр.)
DEMO
function makeThisExtend(obj, CtorFunc) {
for (var k in obj)
if ({}.hasOwnProperty.call(obj, k))
obj[k] = { value: obj[k] };
return Object.create(CtorFunc.prototype, obj);
}
var objFromServer = { Number: "123", id: 5 };
objFromServer = makeThisExtend(objFromServer, PhoneNumber);
alert(objFromServer.Number + " " + objFromServer.id); //123 5
alert(objFromServer.constructor); //function PhoneNumber ...