นี่เป็นช็อตสั้น ๆ เพื่อแสดงวิธีที่ต่างกันเล็กน้อย พวกเขาไม่ได้ "สมบูรณ์" และเป็นข้อจำกัดความรับผิดชอบฉันไม่คิดว่าเป็นความคิดที่ดีที่จะทำเช่นนี้ รหัสก็ไม่สะอาดเหมือนกันเพราะฉันเพิ่งพิมพ์มันด้วยกันอย่างรวดเร็ว
ยังเป็นหมายเหตุ: แน่นอนชั้นเรียน deserializable ต้องมีการก่อสร้างเริ่มต้นเป็นกรณีในภาษาอื่น ๆ ทั้งหมดที่ฉันตระหนักถึง deserialization ทุกชนิด แน่นอนว่า Javascript จะไม่บ่นหากคุณเรียกใช้ตัวสร้างที่ไม่ใช่ค่าเริ่มต้นโดยไม่มีข้อโต้แย้ง แต่คลาสจะต้องเตรียมให้ดีกว่าเดิม (รวมทั้งมันจะไม่เป็น
ตัวเลือก # 1: ไม่มีข้อมูลรันไทม์เลย
ปัญหาของวิธีนี้ส่วนใหญ่แล้วชื่อของสมาชิกใด ๆ จะต้องตรงกับคลาสของมัน ซึ่งจะ จำกัด คุณไว้ที่สมาชิกประเภทเดียวกันหนึ่งคนต่อชั้นเรียนโดยอัตโนมัติและแบ่งกฎการฝึกฝนที่ดีหลายข้อ ฉันขอแนะนำอย่างยิ่งกับเรื่องนี้ แต่เพียงแค่เขียนไว้ที่นี่เพราะมันเป็น "ฉบับร่าง" ครั้งแรกเมื่อฉันเขียนคำตอบนี้ (ซึ่งเป็นสาเหตุที่ชื่อมีชื่อว่า "ฟู" เป็นต้น)
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);
ตัวเลือก # 2: คุณสมบัติชื่อ
เพื่อกำจัดปัญหาในตัวเลือก # 1 เราจำเป็นต้องมีข้อมูลบางอย่างของชนิดของโหนดในวัตถุ JSON ปัญหาคือใน typescript สิ่งเหล่านี้เป็นคอมไพล์เวลาคอมไพล์และเราต้องการพวกมันตอนรันไทม์ - แต่อ็อบเจกต์รันไทม์ไม่ได้ตระหนักถึงคุณสมบัติของมันจนกว่าพวกมันจะถูกตั้งค่า
วิธีหนึ่งที่จะทำได้คือการทำให้ชั้นเรียนรู้ชื่อของพวกเขา คุณต้องใช้คุณสมบัตินี้ใน JSON เช่นกัน ที่จริงแล้วคุณต้องการเพียงแค่ใน 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);
ตัวเลือก # 3: ระบุประเภทสมาชิกอย่างชัดเจน
ดังที่ระบุไว้ข้างต้นข้อมูลประเภทของสมาชิกคลาสไม่สามารถใช้งานได้ในขณะรันไทม์นั่นคือถ้าเราไม่ทำให้มันพร้อมใช้งาน เราต้องทำสิ่งนี้สำหรับสมาชิกที่ไม่ใช่สมาชิกดั้งเดิมและเรายินดีที่จะไป:
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);
ตัวเลือก # 4: verbose แต่วิธีเรียบร้อย
อัพเดท 2016/01/03:ในฐานะที่เป็น @GameAlchemist ชี้ให้เห็นในส่วนความเห็น ( ความคิด , การดำเนินงาน ) ณ typescript 1.7 วิธีการแก้ปัญหาที่อธิบายด้านล่างสามารถเขียนได้ในทางที่ดีใช้ตกแต่งชั้น / คุณสมบัติ
การทำให้เป็นอันดับเป็นปัญหาเสมอและในความคิดของฉันวิธีที่ดีที่สุดคือวิธีที่ไม่สั้น จากตัวเลือกทั้งหมดนี่คือสิ่งที่ฉันต้องการเพราะผู้เขียนชั้นเรียนมีการควบคุมอย่างเต็มที่กับสถานะของวัตถุ deserialized ถ้าฉันต้องเดาฉันจะบอกว่าตัวเลือกอื่น ๆ ไม่ช้าก็เร็วจะทำให้คุณเดือดร้อน (ยกเว้นว่าจาวาสคริปต์ใช้วิธีจัดการกับสิ่งนี้)
จริงๆแล้วตัวอย่างต่อไปนี้ไม่ได้ทำเพื่อความยืดหยุ่น มันแค่คัดลอกโครงสร้างของคลาสจริงๆ อย่างไรก็ตามความแตกต่างที่คุณต้องจำไว้ที่นี่คือว่าคลาสนั้นมีการควบคุมเต็มรูปแบบเพื่อใช้ JSON ชนิดใดก็ได้ที่ต้องการควบคุมสถานะของคลาสทั้งหมด (คุณสามารถคำนวณสิ่งต่าง ๆ เป็นต้น)
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);