skip to Main Content

I’m receiving values in JSON from an API, which can be erroneous because of legacy code that apparently won’t be fixed. These values are being deserialized pretty simply by casting the JSON to an interface. This is all well and good. The problem is I’d like to fix those erroneous values straight at the source during deserialization so that I don’t have to deal with them. An example to illustrate:

enum Bar {
  bar = "bar",
  qux = "qux",
}

// Assume we have multiple interfaces using Bar such that it'd be
// better for the serialization to be defined in Bar
interface Data1 {
  foo: Bar[];
}
interface Data2 {
  foo: Bar[];
}

// All data fetching is done using generics such that changing 
// it would need a full rework
function fetchData<T>() {
  // We expect bar but sometimes receive baz, which would be better deserialized as bar
  // {"foo": [ "bar", "baz", "qux"]}
  const response = await fetch("data.json");
  return await response.json() as T;
}

const data: Data1 = fetchData<Data1>();

I unfortunately can’t define the enum as having multiple values for the same key. I could make different interfaces for what we receive and what we actually work with so that the data is transformed properly but that would require a rework of many things so I’m looking for a way to only affect that specific enum deserialization.

2

Answers


  1. You can create a function to intercept and clean up the JSON data as it’s being parsed and this way you can fix erroneous values at the source-

    enum Bar {
    
    
     bar = "bar",
      qux = "qux",
    }
    
    interface Data1 {
      foo: Bar[];
    }
    interface Data2 {
      foo: Bar[];
    }
    
    // This function will replace any invalid values in Bar fields with "bar"
    function customReviver(key: string, value: any) {
      if (Array.isArray(value) && key === "foo") {
        return value.map((item) => (item === "baz" ? Bar.bar : item));
      }
      return value;
    }
    
    const json = '{"foo": [ "bar", "baz", "qux"]}';
    
    // Clean up values using customReviver function
    const data1: Data1 = JSON.parse(json, customReviver);
    const data2: Data2 = JSON.parse(json, customReviver);
    
    console.log(data1);
    console.log(data2);
    
    Login or Signup to reply.
  2. You will have to define a customDeserializer that accepts json string and a custom transformation function e.g.

    function customDeserializer<T>(json: string, transform: (key: string, value: any) => any): T {
      return JSON.parse(json, transform);
    }
    
    function transformEnum(key: string, value: any): any {
      if (value === "baz") {
        return Bar.bar;
      }
      return value;
    }
    

    Then you can initialize data1 and data2 using customDeserializer instead of JSON.parse

    
    const json = '{"foo": [ "bar", "baz", "qux"]}';
    const data1: Data1 = customDeserializer<Data1>(json, transformEnum);
    const data2: Data2 = customDeserializer<Data2>(json, transformEnum);
    

    I tried this in the typescript-playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search