2012-01-09 48 views
16

Khi sử dụng AJAX, tôi có xu hướng truyền đối tượng từ máy chủ của tôi sang Javascript dưới dạng đối tượng JSON (aka Javascript). Một số chức năng trong Javascript của tôi dựa vào loại đối tượng cụ thể mà tôi đang sử dụng. Ví dụ: cho phép sử dụng số điện thoại chẳng hạn. Tôi có một hàm tạo:Ánh xạ đối tượng JSON cho đối tượng 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; 
} 

Tôi sử dụng khi tạo đối tượng số điện thoại trong Javascript của mình. Trong một số trường hợp, tôi không tạo đối tượng trong JS, tôi lấy đối tượng từ máy chủ để nó xuất hiện dưới dạng một đối tượng chung với các trường giống hệt nhau. Vì vậy, khi mã của tôi dựa vào loại cụ thể bằng cách sử dụng một cái gì đó như thế này:

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

Trong trường hợp này, tôi kiểm tra tên của hàm tạo đối tượng và đặt các biến nhất định dựa trên tên đó. Nếu đối tượng tôi kiểm tra tên trên là một JSON từ máy chủ, nó chỉ đơn giản là cung cấp cho tôi một phản ứng "đối tượng" chung chung và do đó mã không hoạt động. Tôi nhận được thông tin này bằng cách sử dụng chức năng lập bản đồ cho từng đối tượng như:

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

Điều này làm việc tốt, nhưng với tôi có vẻ hơi dư thừa. Đây có phải là cách tốt nhất để giải quyết vấn đề đối tượng JSON hoặc có giải pháp tốt hơn không? Tôi hiểu rằng đây là một loại câu hỏi "Tôi làm điều này tốt nhất có thể", nhưng tôi lặp lại loại logic này một cách chắc chắn trong mã Javascript của tôi và tôi cũng có thể nhận được một hoặc hai ý kiến ​​khác về việc có hay không cách thích hợp để làm điều này trước khi tôi phải bỏ ra hàng giờ để sửa nó trong tương lai.

EDIT: Tôi đã kết thúc việc chấp nhận một giải pháp jQuery vì tôi tình cờ sử dụng jQuery trong dự án của tôi. Tuy nhiên có nhiều giải pháp mà cũng làm việc cho tôi trước khi tôi tìm thấy tùy chọn jQuery. Họ không hoàn toàn sạch sẽ và hiệu quả.

+4

* "đối tượng JSON (aka Javascript)" * JSON không phải là aka JavaScript. Nó có tên riêng vì nó có cú pháp và đặc tả riêng. –

Trả lời

25

Sau đây yêu cầu bạn phải có các thuộc tính giống nhau trong đối tượng và đối tượng JSON của bạn.

var phoneNumber = $.extend(new PhoneNumber(), yourJSONObject); 

Điều này về cơ bản tạo đối tượng PhoneNumber mới và sau đó sao chép tất cả các thuộc tính từ đối tượng JSON của bạn lên đối tượng đó. Phương pháp $.extend() là từ jQuery, nhưng bạn cũng có thể sử dụng như phương pháp tương tự từ ví dụ: Underscore.js hoặc một trong các thư viện/khung công tác khác của js.

+0

Tôi sử dụng jQuery vì vậy đây có thể là giải pháp dễ dàng hơn cho tôi. – ryandlf

+3

Nếu bạn sử dụng góc cạnh, bạn có thể thực hiện 'var phoneNumber = angular.copy (yourJSONObject, new PhoneNumber())'. Tôi có thể không có cú pháp đúng, nhưng bạn có thể làm điều đó, và đó là hàm bạn muốn! – Chris

+1

Đối với góc cạnh, bạn có thể sử dụng angular.copy(), angular.extend() hoặc angular.merge(). Sao chép các thuộc tính của đối tượng mới của bạn và sau đó nhân bản các thuộc tính của đối tượng nguồn. Mở rộng và hợp nhất đưa các thuộc tính của đối tượng nguồn vào đối tượng mới (thông qua bản sao nông và sâu tương ứng), trong khi vẫn giữ các thuộc tính hiện có của đối tượng mới. http://davidcai.github.io/blog/posts/copy-vs-extend-vs-merge/ – Nhan

1

Nếu không hỗ trợ các trình duyệt cũ hơn là ok, Bạn có thể sử dụng Object.create để thực hiện ánh xạ cho bạn. (Thả shim-ít nhất là shim tại MDN-in sẽ không sửa chữa các trình duyệt cũ, vì shim rằng không chấp nhận tham số thứ hai.)

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 ... 
2

AFAIK, trong tất cả mọi thứ đó là không IE, bạn có thể làm điều này:

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

Thật không may, trình duyệt IE không hỗ trợ __proto__ accessor, vì vậy những gì bạn sẽ cần phải làm là đầu tiên tạo ra một thể hiện sản phẩm nào của đối tượng của bạn, sau đó chỉ cần cop y mọi thứ trên:

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

Cả hai cách tiếp cận này chung chung hơn một chút so với các chức năng mapWhatever của bạn, giữ cho nó khô.

+0

Trông giống như một giải pháp sạch hơn. Đây là những gì tôi đang tìm kiếm, cảm ơn. Giả sử thử nghiệm của tôi hoạt động, điều này trông giống như một câu trả lời được chấp nhận. – ryandlf

+1

Được bình chọn hai năm sau khi Amadan đăng câu trả lời của mình. Người ta không bao giờ nên truy cập nguyên mẫu bằng cách sử dụng __proto__ vì điều này không được chấp nhận: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto –

3

câu hỏi tương tự này có rất nhiều câu trả lời thú vị:

Parse JSON String into a Particular Object Prototype in JavaScript

Dựa tắt câu trả lời riêng của poster, tôi nghĩ rằng đây sẽ là một giải pháp hiệu quả cho bạn:

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

Bạn sẽ cần thêm objectType vào các đối tượng JSON mà bạn đang nhận để xác định lớp javascript bạn muốn khởi tạo. Sau đó, khi bạn gọi hàm này, nó sẽ truyền đối tượng đó đến kiểu đó và sao chép dữ liệu trên (bao gồm biến 'objectType').

Sử dụng ví dụ số điện thoại, mã của bạn sẽ trông như thế này:

// 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); 
Các vấn đề liên quan