0

I have a complex object that I want to serialize and deserialize and obtain an object of the same type.

let workflow = new Workflow();
console.log(`workflow is instanceof Workflow: ${workflow instanceof Workflow}`);
console.log(workflow);

let json = JSON.stringify(workflow);
console.log(json);

let workflow2 = JSON.parse(json) as Workflow;
console.log(workflow2);
console.log(`workflow2 is instanceof Workflow: ${workflow2 instanceof Workflow}`);

let workflow3: Workflow = JSON.parse(json) as Workflow;
console.log(workflow3);
console.log(`workflow3 is instanceof Workflow: ${workflow3 instanceof Workflow}`);

The console output is:

enter image description here

Is there an out of the box solution for this or I need to manually reinstantiate the complex object and set all of its properties?

Nicolae Daian
  • 1,065
  • 3
  • 18
  • 39

2 Answers2

3

You can use an object's fromJSON() together with a reviver function to JSON.parse() to achieve what you want.

For example:

type Serialized<T> = Pick<T, keyof T> & { _type: string };

class Workflow {
  foo: number;

  constructor(foo: number) {
    this.foo = foo;
  }

  public toJSON(): Serialized<Workflow> {
    return {
      _type: this.constructor.name,
      ...this
    };
  }

  public static fromJSON(source: Serialized<Workflow>): Workflow {
    return new Workflow(source.foo);
  }
}

function reviver(key: string, value: any): any {
  if (typeof value === "object" && value && "_type" in value) {
    switch (value._type) {
      case "Workflow": return Workflow.fromJSON(value);
    }
  }
  return value;
}

const w = new Workflow(42);
console.log(w instanceof Workflow);

const s = JSON.stringify(w);
console.log(s);

const w2 = JSON.parse(s, reviver) as Workflow;
console.log(w2.foo);
console.log(w2 instanceof Workflow);

prints:

true
{"_type":"Workflow","foo":42}
42
true

Try it out yourself at the playground!

Martin Poelstra
  • 300
  • 1
  • 6
  • This answer is good, I am going to mark it as accepted. However before I received this answer I implemented Option 4 from here: https://stackoverflow.com/a/22886730/4233941. – Nicolae Daian Aug 05 '19 at 20:25
0

Is there an out of the box solution for this...

I've made an npm module named esserializer to solve this problem: save JavaScript/TypeScript class instance during serialization, in plain JSON format, together with its class name information:

const ESSerializer = require('esserializer');

class Workflow {
  foo:number;
  constructor(foo:number) {
    this.foo = foo;
  }
}

const w = new Workflow(42);
console.log(w instanceof Workflow);

const s = ESSerializer.serialize(w);
console.log(s);

Later on, during the deserialization stage (possibly on another machine), esserializer can recursively deserialize object instance, with all Class/Property/Method information retained, using the same class definition:

const o = ESSerializer.deserialize(s, [Workflow]);
console.log(o.foo); // 42
console.log(o instanceof Workflow); // true
shaochuancs
  • 15,342
  • 3
  • 54
  • 62