-
-
Notifications
You must be signed in to change notification settings - Fork 52
Description
Dear maintainers,
would you mind to support polymorphism when serializing?
Problem
At the moment the schema exactly defines the expected type of the property to serialize. If any subclass adds some more properties to serialize, these are simply ignored. Deserializing does not create the original type of instance but the schema-defined type.
Similar, arrays are serialized based on the first item only.
serializr/src/core/serialize.js
Line 24 in 59fe6e5
| schema = getDefaultModelSchema(thing[0]) |
This behavior does not enable using polymorphism with sub classes and does not work well with the idea of dependency injection.
See this simple example:
const serializr = require("serializr");
class Test {}
class Test2 extends Test {}
class ToDo {}
serializr.createModelSchema(Test, {
id: serializr.primitive(),
name: serializr.primitive()
});
serializr.createModelSchema(Test2, {
id: serializr.primitive(),
name: serializr.primitive(),
name2: serializr.primitive()
});
serializr.createModelSchema(ToDo, {
t: serializr.primitive(),
polymorphData: serializr.object(Test),
listOfTests: serializr.list(serializr.object(Test)),
q: serializr.primitive()
});
const testToDo = new ToDo();
testToDo.t = "grab coffee";
testToDo.q = 5;
testToDo.listOfTests = [];
testToDo.listOfTests.push(((id) => {
const t = new Test();
t.id = id;
t.name = "B hello universe";
return t;
})("ab"));
testToDo.listOfTests.push(((id) => {
const t = new Test2();
t.id = id;
t.name = "C hello family";
t.name2 = "sub class of Test";
return t;
})("ac"));
testToDo.polymorphData = ((id) => {
const t = new Test2();
t.id = id;
t.name = "D polymorphic hello";
t.name2 = "another sub class of Test";
return t;
})("iu");
console.log(testToDo,"\n\n");
console.log(serializr.serialize(serializr.getDefaultModelSchema(ToDo), testToDo));The output is:
ToDo {
t: 'grab coffee',
q: 5,
listOfTests:
[ Test { id: 'ab', name: 'B hello universe' },
Test2 { id: 'ac', name: 'C hello family', name2: 'sub class of Test' } ],
polymorphData:
Test2 {
id: 'iu',
name: 'D polymorphic hello',
name2: 'another sub class of Test' } }
{ t: 'grab coffee',
polymorphData: { id: 'iu', name: 'D polymorphic hello' },
listOfTests:
[ { id: 'ab', name: 'B hello universe' },
{ id: 'ac', name: 'C hello family' } ],
q: 5 }
With the serialized version, all information about instances of Test2 are gone.
Possible solution
The schema for serializing is already stored with the instance constructor.
| return thing.constructor.serializeInfo |
Hence it would be possible to detect the schema of the instance to serialize dynamically. Additionally adding some sort of "class name" to the serialized information would be possible and would help to re-create the proper instance upon deserialization.
So the serialized info could look like:
{
__className__: 'ToDo',
t: 'grab coffee',
polymorphData: {
__className__: 'Test2',
id: 'iu',
name: 'D polymorphic hello'
},
listOfTests: [
{ __className__: 'Test', id: 'ab', name: 'B hello universe' },
{ __className__: 'Test2', id: 'ac', name: 'C hello family', name2: 'sub class of Test' }
],
q: 5
}
The class name is a symbolic "class name identifier" (string), either be set by the defined schema or a decorator. This avoids problems with packers that scramble class names. When reading all schemas before deserialization, an internal map of "class name identifiers" to schemas can be build to find the schema for the "class name identifier". Already each schema contains a factory function that is able to create a new instance for that schema.
serializr.createModelSchema(Test, {
__className__: "Test",
id: serializr.primitive(),
name: serializr.primitive()
});
serializr.createModelSchema(Test2, {
__className__: "Test2",
id: serializr.primitive(),
name: serializr.primitive(),
name2: serializr.primitive()
});Do you mind?
Would you mind going that route and accept PRs implementing this?
May be someone is willing to help?
I cannot promise to, but will try.