Murali
Murali

Reputation: 49

Custom serialization in typescript is not happening as expected (Arrays are getting converted to Objects with spread)

1. The Problem

I am doing custom serialization in TypeScript as shown in example below.

I am serializing all strings as custom types. With the following approach, all the strings are getting replaced fine, but those that are in arrays are getting returned as objects. The expectation is that arrays should stay as arrays in returned new object and it should work fine for the strings that exist in both objects and arrays.

As extra info, I am serializing a custom type other than strings in my project, this is an example of what I am trying to do.

Any inputs?

2. The Code

interface Test {
  a: number;
  b: Array<string>
}

interface Sample {
  x: number;
  y: string;
  z: Array<Test>;
}

let sample: Sample = {
  x: 1,
  y: 'hello',
  z: [{ a: 10, b: ['hello', 'hi'] }]
};

function isObject(obj: any): boolean {
  return obj !== null && typeof obj === 'object';
}

function isString(obj: any): boolean {
  return obj !== null && typeof obj === 'string';
}

console.log(`${JSON.stringify(sample)}`);

function customSerializeRecursive(obj: any): any {
  const customTypePlaceHolder = {};

  for (const entry of Object.entries(obj)) {
    const [key, value] = entry;
    if (isString(value)) {
      customTypePlaceHolder[key] = {
        _type: 'test',
        _value: value.toString()
      };

    } else if (isObject(value)) {
      customTypePlaceHolder[key] = customSerializeRecursive(value);

    }
  }

  const newObj = { ...obj, ...customTypePlaceHolder };

  if (Object.keys(customTypePlaceHolder).length === 0) {
    return obj;
  } else {
    return newObj;
  }
}

const newSample = customSerializeRecursive(sample);
console.log(`${JSON.stringify(newSample)}`);

3. The Output

{
    "x": 1,
    "y": "hello",
    "z": [{"a": 10, "b": ["hello", "hi"]}]
}

{
    "x": 1,
    "y": {"_type": "test", "_value": "hello"},
    "z":{"0": {"a": 10, "b": {"0": {"_type": "test", "_value": "hello"}, "1": {"_type": "test", "_value": "hi"}}}}
}

Expected Output

{
    "x": 1,
    "y": "hello",
    "z": [{"a": 10, "b": ["hello", "hi"]}]
}

{
    "x": 1,
    "y": {"_type": "test", "_value": "hello"},
    "z": [{"a": 10, "b": [{"_type": "test", "_value": "hello"}, {"_type": "test", "_value": "hi"}]}]
}

Upvotes: 2

Views: 498

Answers (1)

D.Dimitrioglo
D.Dimitrioglo

Reputation: 3663

A direct answer to your questing will be like this:

const sample: Record<string, any> = {
    x: 1,
    y: 'hello',
    z: [{ a: 10, b: ['hello', 'hi'] }]
};

function replacer(key, value) {
    if (['_type', '_value'].includes(key)) {
        return value;
    }

    if (typeof value === 'string') {
        return {
            _type: 'test',
            _value: value,
        };
    }

    return value;
}

console.log(JSON.stringify(sample, replacer, 2));

which will give you the following result:

{
  "x": 1,
  "y": {
    "_type": "test",
    "_value": "hello"
  },
  "z": [
    {
      "a": 10,
      "b": [
        {
          "_type": "test",
          "_value": "hello"
        },
        {
          "_type": "test",
          "_value": "hi"
        }
      ]
    }
  ]
}

But my gut tells me that you are trying to invent the wheel, so I would recommend you to check out class-transformer which will help you manipulate plain objects to class and back.

P.S. If you are using typescript try to not use any.

Upvotes: 1

Related Questions