Skip to content

Commit

Permalink
TsSerializer.ts - Fix reference object saving order. There is a bug w…
Browse files Browse the repository at this point in the history
…ith circular references, in typical parent children type lists (treeview). In deserailzeObject the reference wasn't pushed onto the references array until after all properties were recursively processed. This resulted in the wrong references being used. The fix is to first push an undefined object onto the references array, save the index, process the objects properties, then save the newly built object to the references array at the index.

base.json - set target to ES2015 so that the Error classes that are subclass of Error have proper typing, see https://stackoverflow.com/questions/41102060/typescript-extending-error-class

TsSerializer.spec.ts - Add test case for parent - children type lists.  Also make sure any SubModel classes were declared before Model otherwise with target ES2015 would get eferenceError: SubModel is not defined
  • Loading branch information
greg committed Jun 3, 2018
1 parent 9f0611c commit cdcf86b
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 23 deletions.
14 changes: 10 additions & 4 deletions TsSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ export class TsSerializer {
if (!type) {
throw new TypeNotRegisteredError(obj);
}
if (!this.references[type.name]) {
this.references[type.name] = [];
}
// push placeholder
this.references[type.name].push(undefined);
// save index
const index = this.references[type.name].length - 1;

for (let property of Object.keys(obj.__value)) {
transformedObj[property] = this.deserializeObject(obj.__value[property]);
Expand All @@ -226,11 +233,10 @@ export class TsSerializer {
const createdObj = type.factory ?
type.factory(transformedObj) :
Object.assign(new (type as any).ctor(), transformedObj);

this.references[type.name][index] = createdObj;

if (!this.references[type.name]) {
this.references[type.name] = [];
}
this.references[type.name].push(createdObj);


return createdObj;
}
Expand Down
79 changes: 61 additions & 18 deletions test/TsSerializer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,18 +299,20 @@ describe('TsSerializer', () => {
});

it('should serialize a model in a model with a constructor', () => {
// had to declare SubModel before Model with target=ES2015 or else
// would get ReferenceError: SubModel is not defined
@Serializable({
factory: data => new Model()
factory: data => new Model(data.name)
})
class Model {
constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { }
class SubModel {
constructor(public name: string = 'sub model') { }
}

@Serializable({
factory: data => new Model(data.name)
factory: data => new Model()
})
class SubModel {
constructor(public name: string = 'sub model') { }
class Model {
constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { }
}

serializer.serialize(new Model()).should.equal(
Expand Down Expand Up @@ -747,18 +749,20 @@ describe('TsSerializer', () => {
});

it('should deserialize a model in a model with a constructor', () => {
// had to declare SubModel before Model with target=ES2015 or else
// would get ReferenceError: SubModel is not defined
@Serializable({
factory: data => new Model()
factory: data => new Model(data.name)
})
class Model {
constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { }
class SubModel {
constructor(public name: string = 'sub model') { }
}

@Serializable({
factory: data => new Model(data.name)
factory: data => new Model()
})
class SubModel {
constructor(public name: string = 'sub model') { }
class Model {
constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { }
}

const deserialized = serializer.deserialize<Model>(
Expand Down Expand Up @@ -1055,18 +1059,20 @@ describe('TsSerializer', () => {
});

it('should work with models that have constructors', () => {
// had to declare SubModel before Model with target=ES2015 or else
// would get ReferenceError: SubModel is not defined
@Serializable({
factory: data => new Model()
factory: data => new Model(data.name)
})
class Model {
constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { }
class SubModel {
constructor(public name: string = 'sub model') { }
}

@Serializable({
factory: data => new Model(data.name)
factory: data => new Model()
})
class SubModel {
constructor(public name: string = 'sub model') { }
class Model {
constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { }
}

const json = serializer.serialize(new Model());
Expand All @@ -1077,6 +1083,43 @@ describe('TsSerializer', () => {
deserialized.sub.should.be.an.instanceof(SubModel);
deserialized.sub.name.should.equal('sub model');
});
it('should work with recursive circular references', () => {
@Serializable()
class Model {
public id: number;
public description: string;
public items?: Model[];
public parent?: Model;
}
let root = new Model(); root.description = 'root'; root.id = -1;
let first = new Model(); first.description = 'first'; first.id = 179;
let fp = new Model(); fp.id = 78; fp.description = 'unit A';
first.parent = fp;
let fpitem1 = new Model(); fpitem1.description = 'baseline'; fpitem1.id = 177;
fpitem1.parent = fp;
let fpitem2 = new Model(); fpitem2.description = 'lower heatflow'; fpitem2.id = 178;
fpitem2.parent = fp;
let fpitem4 = new Model(); fpitem4.description = 'Up 65 Ma heatflow'; fpitem4.id = 180;
fpitem4.parent = fp;
fp.items = [];
fp.items.push(fpitem1); fp.items.push(fpitem2); fp.items.push(first); fp.items.push(fpitem4);
let fpp = new Model(); fpp.description = 'Multiple Scenarios'; fpp.id = 34;
fpp.items = [];
fpp.items.push(fp);
fp.parent = fpp;
let second = new Model(); second.description = 'second'; second.id = 77;
let sp = new Model(); sp.description = 'second parent'; sp.id = 33;
sp.items = []; sp.items.push(second);
second.parent = sp;
root.items = [];
root.items.push(first);
root.items.push(second);

const json = serializer.serialize(root);
const deserialized = serializer.deserialize<Model>(json);
deserialized.should.deep.equal(root);

});

});

Expand Down
2 changes: 1 addition & 1 deletion tsconfig/base.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "ES2015",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "../build",
Expand Down

0 comments on commit cdcf86b

Please sign in to comment.