I receive a JSON object from an AJAX call to a REST server. This object has property names that match my TypeScript class (this is a follow-on to this question).
What is the best way to initialize it? I don't think this will work because the class (& JSON object) have members that are lists of objects and members that are classes, and those classes have members that are lists and/or classes.
But I'd prefer an approach that looks up the member names and assigns them across, creating lists and instantiating classes as needed, so I don't have to write explicit code for every member in every class (there's a LOT!)
I've created a tool that generates TypeScript interfaces and a runtime "type map" for performing runtime typechecking against the results of
JSON.parse
: ts.quicktype.ioFor example, given this JSON:
quicktype produces the following TypeScript interface and type map:
Then we check the result of
JSON.parse
against the type map:I've left out some code, but you can try quicktype for the details.
Another option using factories
https://github.com/MrAntix/ts-deserialize
use like this
you can use
Object.assign
I don't know when this was added, I'm currently using Typescript 2.0.2, and this appears to be an ES6 feature.here's
HalJson
here's what chrome says it is
so you can see it doesn't do the assign recursively
For simple objects, I like this method:
Leveraging the ability to define properties in the constructor lets it be concise.
This gets you a typed object (vs all the answers that use Object.assign or some variant, which give you an Object) and doesn't require external libraries or decorators.
TLDR: TypedJSON (working proof of concept)
The root of the complexity of this problem is that we need to deserialize JSON at runtime using type information that only exists at compile time. This requires that type-information is somehow made available at runtime.
Fortunately, this can be solved in a very elegant and robust way with decorators and ReflectDecorators:
Recording Type-Information
With a combination of ReflectDecorators and property decorators, type information can be easily recorded about a property. A rudimentary implementation of this approach would be:
For any given property, the above snippet will add a reference of the constructor function of the property to the hidden
__propertyTypes__
property on the class prototype. For example:And that's it, we have the required type-information at runtime, which can now be processed.
Processing Type-Information
We first need to obtain an
Object
instance usingJSON.parse
-- after that, we can iterate over the entires in__propertyTypes__
(collected above) and instantiate the required properties accordingly. The type of the root object must be specified, so that the deserializer has a starting-point.Again, a dead simple implementation of this approach would be:
The above idea has a big advantage of deserializing by expected types (for complex/object values), instead of what is present in the JSON. If a
Person
is expected, then it is aPerson
instance that is created. With some additional security measures in place for primitive types and arrays, this approach can be made secure, that resists any malicious JSON.Edge Cases
However, if you are now happy that the solution is that simple, I have some bad news: there is a vast number of edge cases that need to be taken care of. Only some of which are:
If you don't want to fiddle around with all of these (I bet you don't), I'd be glad to recommend a working experimental version of a proof-of-concept utilizing this approach, TypedJSON -- which I created to tackle this exact problem, a problem I face myself daily.
Due to how decorators are still being considered experimental, I wouldn't recommend using it for production use, but so far it served me well.
JQuery .extend does this for you: