0

I've run into an issue with the serialization of classes that I don't know how to handle.

I create objects from REST or database requests requests like this:

export interface ILockerModel {
    id: string
    lockerId: string
    ownerId: string
    modules: IModuleModel[]
}
export class LockerModel implements ILockerModel {
    private _id: string
    private _lockerId: string
    private _ownerId: string
    private _modules: ModuleModel[]

    constructor(document: ILockerModel) {
        this._id = document.id
        this._lockerId = document.lockerId
        this._ownerId = document.ownerId
        this._modules = document.modules.map(m => new ModuleModel(m))
    }
    // Utility methods
}

I then have multiple utility methods that make it easier to work with the model, adding and removing things from lists and so on.

When I'm done I want to persist the object to a document database or return it in a REST response, so I call JSON.stringify(objectInstance). However, this gives me the class but with all properties underscored (_), not my getter values. This breaks the deserialization in other parts of my application.

Serializing the interface gives me what I want, but I haven't found a straightforward way to go from the class to the interface representation. The issue gets tougher because I deserialize the data in a hierarchy (see the modules mapping in the constructor).

How do you usually solve this issue?

jokarl
  • 1,913
  • 2
  • 24
  • 52

1 Answers1

1

As far as I can see you do not really implement the ILockerModel. Shouldnt this throw an error?

When I run it, I get the following:

Type 'LockerModel' is missing the following properties from type 'ILockerModel': id, lockerId, ownerId, modules

The other thing is that JSON.strigify() just takes your object and makes a string representation of all its properties. It does not care about your getters. If you want it to transform it to the right format, you should give it an object in the correct format.

One solution would be to just remove the '_' from all the keys, by using a combination of map and reduce:

const input = {
  _test: 123,
  _hello: 'world'
};

console.log(input);
console.log(JSON.stringify(input));

const convertToJson = (obj) => {
  return Object.entries(obj) // Create array from object
    .map(([key, value]) => [  // change key to remove '_'
      key.startsWith('_') ? key.substring(1) : key, 
      value
    ])
    .reduce((acc, [key, value]) => { // Transform back to object
      acc[key] = value;
      return acc;
    }, {});
}

const output = convertToJson(input);


console.log(output);
console.log(JSON.stringify(output));

Or if you are allowed to use ES10:

const input = {
  _test: 123,
  _hello: 'world'
};

console.log(input);
console.log(JSON.stringify(input));

const convertToJson = (obj) => {
  return Object.fromEntries( // Create Object from array
    Object.entries(obj) // Create array from object
      .map(([key, value]) => [ // change key to remove '_'
        key.startsWith('_') ? key.substring(1) : key, 
        value
      ])
  );
}

const output = convertToJson(input);


console.log(output);
console.log(JSON.stringify(output));
MauriceNino
  • 6,214
  • 1
  • 23
  • 60
  • Wouldn't you just create getters? See: https://stackoverflow.com/a/12850536/1762224, but this is more inline with what you are trying to accomplish, but safer for TS: https://stackoverflow.com/a/44315652/1762224 – Mr. Polywhirl Aug 25 '20 at 13:24
  • The interface is correctly implemented, I have the strictest checks on. Unfortunately I am limited in ES versions since this runs in Azure Functions. The function removing underscores looks like a good-enough solution, I'll give it a try. Thank you! – jokarl Aug 25 '20 at 13:31