Search code examples
jsonangulartypescriptcastingjsonserializer

Casting JSON to complex types


I am trying to cast my http.get response to actual object -> in my specific case array of complex objects.

In a normal scenario, where you don't need any specific casting, you could do the following (simplified):

return this.httpClient.get(api, this._options_get)
  .pipe(
    map((response: any) => {
      return response.value as NewProduct[];
    })
  );

As my need is to actually cast this to an object, I have created this static method which does that:

static toProduct(otherProduct: any): NewProduct {
    let item = new NewProduct();

    Object.keys(otherProduct).forEach(prop => {
        if (typeof otherProduct[prop] === "object" && otherProduct[prop]) {
            if (!item.hasOwnProperty(prop))
                item[prop] = otherProduct[prop];
            Object.assign(item[prop], otherProduct[prop]);
        }
        else
            item[prop] = otherProduct[prop];
    })

    return item;
}

Under Object.assign I am taking already existing object which was initialized under first line and I am simply copying all the properties from the otherProduct to it. However I start to face problem when it comes to array of objects. Example (with simplified class):

export class Person {
    name:string;
    age:number;
    addresses:Address[] = [];
}
export class Address {
    street:string;
    city:string;
    fullAddress() : string { return this.street + this.city; }
}

As soon as I have this sort of array, I don't have any initial object in item. This means that there is no initial constructor of a class which results in simple Object. This is no error for JavaScript or TypeScript; however when I am trying to access internal method of a class (in our simplified case fullAddress(), I won't be able to.

The reason why I need that is that I am overriding toString() method on my sub-classes, which is necessary for MatTableDataSource when you use the filter method (which works with strings).

Is there a way how to retrieve elements from http.get() and properly map results to typed objects?


Solution

  • You're being too generic. You're creating objects of objects, not objects of Product with children of Addresses.

    If you want to create a new product you're going to have to understand the relationship between the api's results and the data you want in the UI.

    Because you're using classes and not interfaces and want to inherit the functions, the only way to get new Addresses into the new object is with the new keyword.

    And you'll have to loop through. You're not going to find a shortcut for this. You're going to need to loop through the data and transform it. If your api is giving you an ApiPerson then you'll want to do something like this:

    const addresses = apiPerson.addresses.map((apiAddress) => {
        const address = new Address();
        // map properties of apiAddress to address...
        return address;
    });
    

    Now that you have the addresses, you can map the apiPerson to a new Person()'s properties and then set the newPerson.addresses = address.