1

I have a fairly substantial project in typescript that uses a lot of object-oriented programming, resulting in a nontrivial object graph (including some objects indirectly pointing to themselves). I now need to serialize this graph to a string. Is there any way (in typescript) to serialize such a graph without losing information upon de-serialization?

Ideally, I would like a solution with as little mandatory bookkeeping on each new class as possible.

Things I've tried:

  1. JSOG. This didn't work because when it de-serializes the objects, it just leaves them as type Object, and they lose their methods.

  2. TypedJSON. This didn't work because it fails with Uncaught TypeError: cyclic object value

  3. An unholy combination of the two: This didn't work because even though I can pass something created by jsog-typescript into TypedJSON, TypedJSON will reject it as not being the correct type.

RWeb
  • 45
  • 7
  • Functions are not serializable. You can stringify them, but this does not maintain any references in the outer lexical scope (closures). – jsejcksn Feb 06 '22 at 09:06
  • 1
    I know that. I just a way to get the functions back when I deserialize my objects. – RWeb Feb 06 '22 at 17:52
  • I've made an npm module named esserializer (https://www.npmjs.com/package/esserializer) to solve this problem: save JavaScript class instance values during serialization, in JSON format. And later on, during the deserialization stage (possibly on another machine), esserializer can recursively deserialize object instance, with all Class/Property/Method information retained, and with cyclical reference structure supported. – shaochuancs Feb 10 '22 at 09:49

3 Answers3

0

I ended up writing my own serializer

RWeb
  • 45
  • 7
0

I also thought about the circular reference problem and the class at the same time, and just made a package.

https://github.com/denostack/superserial

The best example of this library:

import { Serializer } from 'superserial'

const serializer = new Serializer()

{
  // recursive map
  const map = new Map();
  map.set(map, map);

  const serialized = serializer.serialize(map);
  console.log(serialized); // output: Map($0=>$0)

  const deserialized = serializer.deserialize("Map($0=>$0)");

  console.log(deserialized === [...deserialized.keys()][0]); // true
  console.log(deserialized === deserialized.get([...deserialized.keys()][0])); // true
}

{
  // nested object and recursive
  const obj = { foo: { bar: {} } }
  obj.foo.bar.baz = obj

  const serialized = serializer.serialize(obj);
  console.log(serialized); // output: {"foo":$1};{"bar":$2};{"baz":$0}

  const deserialized = serializer.deserialize('{"foo":$1};{"bar":$2};{"baz":$0}');

  console.log(deserialized) // output: <ref *1> { foo: { bar: { baz: [Circular *1] } } }
  console.log(deserialized.foo.bar.baz === deserialized) // true
}
wan2land
  • 74
  • 4
0

Might be a little bit late for the author but still useful for someone.

We had exact same issue with circular references and used lot of OOP stuff with polymorphic structures. Many existing serializers like class-transformer and TypedJSON was not able to handle all our use cases in convenient way.

We also ended up writing our own serializer which is now open-source:

https://github.com/dipscope/TypeManager.TS

It has several strategies for handling circular object references and allows customization.

dpimonov
  • 61
  • 5