Angular 8 – Typescript Auto Reverse Enum Mapping (Json – Enum Type)

I want to map JSON response to TypeScript class. It should also work if the class has some Enum types. Too many demands here and we are going to achieve them all.

First thing’s first, we’ll be using a small library called json2typescript for mapping JSON object to an instance of Typescript class. We need this because during JavaScript runtime there is no way to type check and map JSON objects to TypeScript classes.

We can utilize decorators provided by the library to map JSON to existing classes.

  • Classes need to be preceded by @JsonObject(classIdentifier)
  • Properties need to be preceeded by @JsonProperty(jsonProperty, conversionOption, isOptional)
  • Properties need to have a default value (or undefined), otherwise, the mapper will not work

Let consider an example.

{
    "PropertyA": "Example",
    "PropertyB": true,
    "PropertyC": 1,
    "PropertyD": 2
}

If you look at this TypeScript class to which we are going to map our JSON object, everything is quite self-explanatory. One thing that I want to highlight is the custom JSON converter for Enum types. json2typescript library provides a way to create and use custom conversion between JSON objects and TypeScript objects.

@JsonObject()
export class CustomObject {
  @JsonProperty('PropertyA', String, true) propertyA: string = undefined;
  @JsonProperty('PropertyB', Boolean, true) propertyB: boolean = undefined;
  @JsonProperty('PropertyC', PropertyCEnumConverter, true) propertyC: PropertyCEnum = undefined;
  @JsonProperty('PropertyD', PropertyDEnumConverter, true) propertyD: PropertyDEnum = undefined;
}

We can create a custom Enum Converter by decorating a TypeScript class with @JsonConverter and implementing the JsonCustomConvert class. The implementation mandates writing serializing and deserializing logic i.e. we’ll have to fill up serialize() and deserialize() methods. In our specific case, the serialize() method converts Enum to string and the deserialize() method handles converting JSON value to respective Enum.

@JsonConverter
export class EnumConverter<T> implements JsonCustomConvert<any> {
  enumType: {[key: string]: any};

  constructor(enumType: {[key: string]: any}) {
    this.enumType = enumType;
  }

  serialize(data: any): string {
    if (!data) {
      return null;
    }
    return data.toString(); // Return as a number if that is desired
  }

  deserialize(data: any): T {
    if (data === undefined || data == null) {
      return undefined;
    }
    for (const property of Object.keys(this.enumType)) {
      if (property.toUpperCase() === String(data).toUpperCase()) {
        const enumMember = this.enumType[property];
        if (typeof enumMember === 'string') {
          return <T>this.enumType[<string>enumMember];
        }
      }
    }
    return undefined;
  }
}

In most cases, creating a converter like we just did would have been sufficient. But, since we need to pass in our Enum objects, we’ll have to create separate Enum converters for each of our Enum types. These classes will extend the main EnumConverter class which we created earlier.

export enum PropertyCEnum {
  ABC = 0,
  DEF = 1
}
export class PropertyCEnumConverter extends EnumConverter<PropertyCEnum> {
  constructor() {
    super(PropertyCEnum);
  }
}

export enum PropertyDEnum {
  GHI = 0,
  JKL = 1,
  MNO = 2
}
export class PropertyDEnumConverter extends EnumConverter<PropertyDEnum> {
  constructor() {
    super(PropertyDEnum);
  }
}

Well, that’s it. By using the power of json2typescript and some custom implementation for converting values into Enum, we were able to auto-reverse map JSON into TypeScript Enum.

Let me know if this post was helpful and/or requires any improvements.

Join the ConversationLeave a reply

Your email address will not be published. Required fields are marked *

Comment*

Name*

Website

2 Comments

  1. Tomek

    Your post was very helpful for me, but creating a new class for each enum is a little bit annoying. I’ve extended your EnumConverter with factory method, which builds a class with given enum (unfortunately I haven’t found option to add code snippet in comment):

    public static build(enumType: { [key: string]: any }) {
    return class extends EnumConverter {
    constructor() {
    super(enumType);
    }
    };
    }

    Now in filed annotation you can write:

    @JsonProperty(‘PropertyC’, EnumConverter.build(PropertyCEnum), true) propertyC: PropertyCEnum = undefined;

  2. Sagun Pandey

    I wonder why I didn’t think about that. Thank you for such an elegant update to the solution. I am sure the future reader will find it super helpful.