How do I initialize a typescript object with a JSON object -
i receive json object ajax call rest server. object has property names match typescript class (this follow-on this question).
what best way initialize it? don't think this work because class (& json object) have members lists of objects , members classes, , classes have members lists and/or classes.
but i'd prefer approach looks member names , assigns them across, creating lists , instantiating classes needed, don't have write explicit code every member in every class (there's lot!)
these quick shots @ show few different ways. no means "complete" , disclaimer, don't think it's idea this. code isn't clean since typed rather quickly.
also note: of course deserializable classes need have default constructors case in other languages i'm aware of deserialization of kind. of course, javascript won't complain if call non-default constructor no arguments, class better prepared (plus, wouldn't "typescripty way").
option #1: no run-time information @ all
the problem approach name of member must match class. automatically limits 1 member of same type per class , breaks several rules of practice. advise against this, list here because first "draft" when wrote answer (which why names "foo" etc.).
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);
option #2: name property
to rid of problem in option #1, need have kind of information of type node in json object is. problem in typescript, these things compile-time constructs , need them @ runtime – runtime objects have no awareness of properties until set.
one way making classes aware of names. need property in json well, though. actually, only need in 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);
option #3: explicitly stating member types
as stated above, type information of class members not available @ runtime – unless make available. need non-primitive members , go:
interface deserializable { gettypes(): object; } class member implements deserializable { id: number; gettypes() { // since member, id, primitive, don't need // return here return {}; } } class exampleclass implements deserializable { mainid: number; firstmember: member; secondmember: member; gettypes() { return { // duplication 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);
option #4: verbose, neat way
update 01/03/2016: @gamealchemist pointed out in comments, of typescript 1.7, solution described below can written in better way using class/property decorators.
serialization problem , in opinion, best way way isn't shortest. out of options, i'd prefer because author of class has full control on state of deserialized objects. if had guess, i'd other options, sooner or later, in trouble (unless javascript comes native way dealing this).
really, following example doesn't flexibility justice. copy class's structure. difference have keep in mind here, though, class has full control use kind of json wants control state of entire class (you calculate things etc.).
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);
Comments
Post a Comment