user3833308
user3833308

Reputation: 1212

Typescript class customize JSON serialization & deserialization

I have a wrapper class in TS that accepts a string in constructor and converts into a bigint internally. I want to customize the serialization/deserialization of this class's object,

export class MyInt64 implements MyDataTypes {
    private readonly _internal: BigInt;

    constructor(val: string) {
        this._internal = BigInt(val);
    }
    toJSON() {
        return {
            val: this._internal,
        };
    }
}

when JSON.stringify(new MyInt64("9223372036854775807")) gets called I want it to not round the number down to 9223372036854776000.

How to do this?


Edit

For my type MyInt64 I want the JSON.stringify() yield into string and that I can do it by overriding toJSON() and keeping track of an internal string variable.

However I want to do MyJSON.stringify() which wraps JSON.stringify() and converts the string value of MyInt64 to number.


Clarification

export interface IUser {
    id: MyInt64;
    name: string;
    balance: number;
}
const user : IUser = {
                id: new MyInt64("9223372036854775807"),
                name: "Alice",
                balance: 123
};

// normal framework serialization & deserialization
const userString1 = JSON.stringify(user);
// This is expected to be {"id":"9223372036854775807","name":"Alice","balance":123}


const userStringCustom = JSON.stringify(user, (k, v) => {
    if (value instanceof MyInt64) {
     // call JSONBig.stringify(value) using json-bingint lib
    }
});
// This is expected to be {"id":9223372036854775807,"name":"Alice","balance":123}


Upvotes: 0

Views: 1972

Answers (1)

jmrk
jmrk

Reputation: 40521

One easy way would be:

  toJSON() {
    return { val: this._internal.toString() };
  }

You may want to include some sort of type identifier, so that upon deserialization you can tell these objects apart from anything else that's serialized as {val: "some_string_which_may_be_numeric"}.

If your BigInts are large (let's say, hundreds or thousands of bits), you can improve performance by using hex strings (because conversions between internal binary number representations and hex strings are easy and fast, whereas conversions between binary numbers and decimal strings are expensive computational work). Based on your class name "MyInt64" I'm guessing that the difference won't be measurable for your case; I'm mentioning the idea here for completeness and future readers.

Put together, that'd give something like:

  toJSON() {
    return {
      type: "MyInt64",
      val: "0x" + this._internal.toString(16),
    };
  }

For deserialization, you can use a reviver function, something like:

JSON.parse(..., (key, value) => {
  if (typeof value === "object" && value.type === "MyInt64") {
    return new MyInt64(value.val);
  }
  // Optionally handle other special cases here...
  return value;
}

Upvotes: 1

Related Questions