Đây là một số bức ảnh nhanh ở đây để hiển thị một vài cách khác nhau. Chúng hoàn toàn không phải là "hoàn thành" và như một sự từ chối, tôi không nghĩ rằng đó là một ý tưởng tốt để làm điều đó như thế này. Ngoài ra, mã không quá sạch vì tôi chỉ cần gõ nó với nhau khá nhanh.
Cũng như một lưu ý: Tất nhiên các lớp khử lưu huỳnh cần phải có các hàm tạo mặc định như trường hợp của tất cả các ngôn ngữ khác, nơi tôi nhận thức được việc khử lưu huỳnh dưới bất kỳ hình thức nào. Tất nhiên, Javascript sẽ không phàn nàn nếu bạn gọi một hàm tạo không mặc định, không có đối số, nhưng lớp tốt hơn nên được chuẩn bị cho nó sau đó (cộng với, nó sẽ không thực sự là "cách đánh máy").
Tùy chọn # 1: Không có thông tin về thời gian chạy
Vấn đề với cách tiếp cận này chủ yếu là tên của bất kỳ thành viên nào phải phù hợp với lớp của nó. Điều này tự động giới hạn bạn với một thành viên cùng loại trên mỗi lớp và phá vỡ một số quy tắc thực hành tốt. Tôi thực sự khuyên bạn nên chống lại điều này, nhưng chỉ liệt kê nó ở đây vì đó là "bản nháp" đầu tiên khi tôi viết câu trả lời này (đó cũng là lý do tại sao các tên là "Foo", v.v.).
module Environment {
export class Sub {
id: number;
}
export class Foo {
baz: number;
Sub: Sub;
}
}
function deserialize(json, environment, clazz) {
var instance = new clazz();
for(var prop in json) {
if(!json.hasOwnProperty(prop)) {
continue;
}
if(typeof json[prop] === 'object') {
instance[prop] = deserialize(json[prop], environment, environment[prop]);
} else {
instance[prop] = json[prop];
}
}
return instance;
}
var json = {
baz: 42,
Sub: {
id: 1337
}
};
var instance = deserialize(json, Environment, Environment.Foo);
console.log(instance);
Tùy chọn # 2: Thuộc tính tên
Để loại bỏ vấn đề trong tùy chọn # 1, chúng ta cần có một số loại thông tin về loại nút trong đối tượng JSON. Vấn đề là trong Bản mô tả, những thứ này là các cấu trúc thời gian biên dịch và chúng ta cần chúng trong thời gian chạy - nhưng các đối tượng thời gian chạy đơn giản là không có nhận thức về các thuộc tính của chúng cho đến khi chúng được đặt.
Một cách để làm điều đó là bằng cách làm cho các lớp nhận biết tên của họ. Bạn cũng cần thuộc tính này trong JSON. Trên thực tế, bạn chỉ cần nó trong json:
module Environment {
export class Member {
private __name__ = "Member";
id: number;
}
export class ExampleClass {
private __name__ = "ExampleClass";
mainId: number;
firstMember: Member;
secondMember: Member;
}
}
function deserialize(json, environment) {
var instance = new environment[json.__name__]();
for(var prop in json) {
if(!json.hasOwnProperty(prop)) {
continue;
}
if(typeof json[prop] === 'object') {
instance[prop] = deserialize(json[prop], environment);
} else {
instance[prop] = json[prop];
}
}
return instance;
}
var json = {
__name__: "ExampleClass",
mainId: 42,
firstMember: {
__name__: "Member",
id: 1337
},
secondMember: {
__name__: "Member",
id: -1
}
};
var instance = deserialize(json, Environment);
console.log(instance);
Tùy chọn # 3: Rõ ràng nêu rõ các loại thành viên
Như đã nêu ở trên, thông tin loại của các thành viên lớp không có sẵn trong thời gian chạy - đó là trừ khi chúng tôi cung cấp thông tin đó. Chúng tôi chỉ cần làm điều này cho các thành viên không nguyên thủy và chúng tôi rất tốt để đi:
interface Deserializable {
getTypes(): Object;
}
class Member implements Deserializable {
id: number;
getTypes() {
// since the only member, id, is primitive, we don't need to
// return anything here
return {};
}
}
class ExampleClass implements Deserializable {
mainId: number;
firstMember: Member;
secondMember: Member;
getTypes() {
return {
// this is the duplication so that we have
// run-time type information :/
firstMember: Member,
secondMember: Member
};
}
}
function deserialize(json, clazz) {
var instance = new clazz(),
types = instance.getTypes();
for(var prop in json) {
if(!json.hasOwnProperty(prop)) {
continue;
}
if(typeof json[prop] === 'object') {
instance[prop] = deserialize(json[prop], types[prop]);
} else {
instance[prop] = json[prop];
}
}
return instance;
}
var json = {
mainId: 42,
firstMember: {
id: 1337
},
secondMember: {
id: -1
}
};
var instance = deserialize(json, ExampleClass);
console.log(instance);
Tùy chọn # 4: Cách dài dòng, nhưng gọn gàng
Cập nhật 01/03/2016: Như @GameAlchemist đã chỉ ra trong các ý kiến ( ý tưởng , triển khai ), kể từ Bản mô tả 1.7, giải pháp được mô tả dưới đây có thể được viết theo cách tốt hơn bằng cách sử dụng trang trí lớp / thuộc tính.
Tuần tự hóa luôn là một vấn đề và theo tôi, cách tốt nhất là cách không ngắn nhất. Trong số tất cả các tùy chọn, đây là điều tôi thích vì tác giả của lớp có toàn quyền kiểm soát trạng thái của các đối tượng được khử lưu huỳnh. Nếu tôi phải đoán, tôi sẽ nói rằng tất cả các tùy chọn khác, sớm hay muộn, sẽ khiến bạn gặp rắc rối (trừ khi Javascript đưa ra một cách riêng để xử lý vấn đề này).
Thực sự, ví dụ sau đây không làm công lý linh hoạt. Nó thực sự chỉ sao chép cấu trúc của lớp. Tuy nhiên, điểm khác biệt bạn phải ghi nhớ ở đây là lớp có toàn quyền kiểm soát để sử dụng bất kỳ loại JSON nào mà nó muốn kiểm soát trạng thái của toàn bộ lớp (bạn có thể tính toán mọi thứ, v.v.).
interface Serializable<T> {
deserialize(input: Object): T;
}
class Member implements Serializable<Member> {
id: number;
deserialize(input) {
this.id = input.id;
return this;
}
}
class ExampleClass implements Serializable<ExampleClass> {
mainId: number;
firstMember: Member;
secondMember: Member;
deserialize(input) {
this.mainId = input.mainId;
this.firstMember = new Member().deserialize(input.firstMember);
this.secondMember = new Member().deserialize(input.secondMember);
return this;
}
}
var json = {
mainId: 42,
firstMember: {
id: 1337
},
secondMember: {
id: -1
}
};
var instance = new ExampleClass().deserialize(json);
console.log(instance);