Closed furqansafdar closed 5 years ago
I assume this is coming from Json.NET (which uses the $type
property with a fully-qualified name by default). TypedJSON is very similar, only it uses __type
by default, which can be configured by using a custom type-resolver. However, one big difference is that TypedJSON does not use a fully-qualified name, but only the class-name, so you'll either need to configure Json.NET to only emit the class name, or write a type-resolver that only takes the last part of the fully qualified name.
The signature of a type-resolver is the following:
(sourceObject: Object, knownTypes: Map<string, Function>) => Function;
Where sourceObject
is a raw, untyped Javascript object (you inspect this object to look for a type-hint), and knownTypes
is a map of references to any additional known classes used during the deserialization process. The return value is the class reference itself. These known types must be set in advance, more on that later.
This is the default type-resolver, which uses the __type
property:
(sourceObject, knownTypes) => knownTypes.get(sourceObject.__type)
You can set a custom type-resolver in the configuration:
new TypedJson(Entity, {
typeResolver: (sourceObject, knownTypes) => knownTypes.get(sourceObject.$type)
});
If Json.NET is emitting fully-qualified names, you should be able to do this:
new TypedJson(Entity, {
typeResolver: (sourceObject, knownTypes) =>
{
if (sourceObject.$type)
{
let typeParts = sourceObject.$type.split(".")
let className = typeParts[typeParts.length - 1];
return knownTypes.get(className);
}
}
});
Now, to recognize sub-classes during deserialization, you need to set them as known types (which is basically a listof classes). You can set this in the configuration, or in the @jsonObject
decorator, this must be done on the class which you expect to contain polymorphic objects:
@jsonObject({ knownTypes: [ContainerMember, SomeConcreteContainerMember1, ... ] })
export class Entity {
@jsonArrayMember(Member)
public members: Member[];
}
Note: you can also specify a static method by its key for knownTypes
, in that case your method should return the array of known-types.
Getting error when decorating @jsonObject
to my abstract classes.
Also when decorating my ContainerMember
with knownTypes:
Argument of type 'typeof ContainerMember' is not assignable to parameter of type 'ParameterlessConstructor<{}>'.
@jsonObject({ knownTypes: [SomeConcreteContainerMember1, ... ] })
export abstract class ContainerMember extends Member {
@jsonArrayMember(Member)
public children: Member[];
}
Is there any detailed documentation available?
Hey,
Thanks for submitting the question, unfortunately the documentation is not as detailed as we would wish, but I encourage you to take a look at the comments on the interfaces and at the specs we have.
The case you are mentioning can be solved immediately in two ways. The first one is the one I would recommend, because it was supported from the beginning. You can wrap the abstract class in a wrapper class that would have the knownTypes
provided. Like in this test (notice the configuration on the Graph
class).
https://github.com/JohnWeisz/TypedJSON/blob/master/spec/polymorphism-abstract-class.spec.ts
You can also sort of hack it(mostly to go around circular dependencies). Instead of calling jsonObject
on the abstract class, you call it as a normal function(after the last concrete member). To go around typescript errors, just assert the type to any
. I have added a test case for that, so that you can take a look.
https://github.com/JohnWeisz/TypedJSON/blob/master/spec/polymorphism-root-abstract-class.spec.ts
In addition to what @Neos3452 suggested, you should also be able to assert the jsonObject
decorator itself to any
:
@(jsonObject as any)({ knownTypes: [SomeConcreteContainerMember1, ... ] })
export abstract class ContainerMember extends Member {
@jsonArrayMember(Member)
public children: Member[];
}
This is not particularly nice or well supported due to limitations in TypeScript type checking (also, dropping jsonObject
on an abstract class like this can actually make TypedJSON instantiate an abstract class if the source JSON specifies so).
Hope, it will be helpful for someone who will search, I made some util function and new decorator to achieve polymorphic behaviour, which worked quite well for me. it is added knownTypes to root type automatically, searching for root element: https://gist.github.com/krizka/c83fb1966dd57997a1fc02625719387d
I have a slightly complex structure where my
Entity
class hasmembers
property which may contains members of eitherContainer
orAction
type. The $type information is also coming along in the json to identify if it is a Container or Action member with every object but how to map it to corresponding classes using this $type information?