Sergio Xalambrí

Use serializers with JSON.stringify and JSON.parse

If you are working with an API which expects and returns keys without using camelCase, e.g if you it uses snake_case, you can use JSON.stringify and JSON.parse to serialize it easily

Serialization

Let's see how to serialize an object to a string and change the keys to snake_case.

import { underscore } from "inflected";

function serialize<Input>(input: Input): string {
  return JSON.stringify(input, (_: string, value: unknown) => {
    // first we need to ensure the value is an object
    // and it's not an array (the typeof an array is object)
    // and it's not null (the typeof null is object)
    if (typeof value === "object" && !Array.isArray(value) && value !== null) {
      // we transform the object to an array with the shape [[key, value]]
      let entries = Object.entries(value).map((entry) => [
        underscore(entry[0]),
        entry[1],
      ]);
      // we get the entries and transform back to an object
      return Object.fromEntries(entries);
    }

    return value;
  });
}

Now this serialize function will receive any value that JSON.stringify could have received and apply our replacer function to transform the keys of the objects (and only objects) to snake_case, to do the transformation I'm using the Inflected package but anything will work.

De-Serialization (aka parsing)

Let's see now how we could receive a JSON string with keys not using camelCase and transform them to an object with the keys in the expected case.

import { camelize } from "inflected";

function parse<Output>(input: string): Output {
  return JSON.parse(input, (_key, value: unknown) => {
    // first we need to ensure the value is an object
    // and it's not an array (the typeof an array is object)
    // and it's not null (the typeof null is object)
    if (typeof value === "object" && !Array.isArray(value) && value !== null) {
      // we transform the object to an array with the shape [[key, value]]
      let entries = Object.entries(value).map((entry) => [
        camelize(entry[0], false),
        entry[1],
      ]);
      // we get the entries and transform back to an object
      return Object.fromEntries(entries);
    }
    return value;
  });
}

As you can see, the process is the same as the one used in serialize but instead of transforming with the underscore function we use camelize, the false as second argument is to use camelCase and not PascalCase which is the default of Inflected.