Adrian
Adrian

Reputation: 84

Json to typescript object . Inheritance

I have three classes

class Device{
    name:string;
}

class Mobile extends Device{
    number:string;
}

class Computer extends Device{
    macAddress:string;
}

and json

[{
'name':'mobile1',
'number':'600 600 600',
'class':'Mobile'
},{
'name':'computer',
'macAddress:'123123123',
'class':'Computer'
}]

is it possible using some kind of decorators/or anything else to get List of devices with correct object types. I'm producting Json at my site so i can also add another fields, change structure to make typescript object list generate corectly

I was searching for any solution without success.

Regards, Adrian

Upvotes: 0

Views: 3739

Answers (2)

Adrian
Adrian

Reputation: 84

I've based my solution on what andreas wrote thank You. To achieve proper solution i've used aswesome library json2typescript . My Solution:

import {JsonObject, JsonProperty, JsonConverter, JsonCustomConvert, JsonConvert} from "json2typescript";


@JsonConverter
class DeviceConverter implements JsonCustomConvert<DeviceDto[]> {

  // We receive the instance and just serialize it with the standard json2typescript method.
  serialize(device: DeviceDto[]): any {
    const jsonConvert: JsonConvert = new JsonConvert();
    return jsonConvert.serialize(device);
  }

  // We receive a json object (not string) and decide
  // based on the given properties whether we want to
  // create an instance of Computer or Mobile.
  deserialize(devicesInput: any): DeviceDto[] {

    const jsonConvert: JsonConvert = new JsonConvert();


    let devices: Array<DeviceDto> = new Array<DeviceDto>();
    for (let device of devicesInput) {
      if (device['type'] == 'mobile') {
        let temp:MobileDeviceDto=jsonConvert.deserialize(device, MobileDeviceDto)
        devices.push(temp);
      } else if (device['type'] == 'rpi') {
        devices.push(jsonConvert.deserialize(device, RaspberryPiDeviceDto));
      }
    }
    return devices;
  }


}

@JsonObject
export class DevicesDto {
  @JsonProperty("devices", DeviceConverter)
  devices: DeviceDto[] = [];
}


@JsonObject
export class DeviceDto {
  @JsonProperty("name", String)
  name: string= undefined;
  @JsonProperty("description", String)
  description: string= undefined;
  @JsonProperty("type", String)
  type: string= undefined;


}

@JsonObject
export class MobileDeviceDto extends DeviceDto {
  @JsonProperty("number", String)
  number: string = undefined;
}

@JsonObject
export class RaspberryPiDeviceDto extends DeviceDto {
  @JsonProperty("version", String)
  version: string = undefined;
}

Usage:

 let jsonConvert: JsonConvert = new JsonConvert();
 let devices: DeviceDto[] = jsonConvert.deserialize(data, DevicesDto).devices;
 this.subjectDeviceList.next(data);

Thank You very much :)

Upvotes: 1

andreas
andreas

Reputation: 8004

I would suggest the following implementation. Please note the comments inside. It might contain some errors because I cannot actually check the code, so maybe some more work is necessary.

Basic idea: To make code simple in your components, you should wrap your array into an object, here called JsonDevices. Then you can write a custom converter and let the magic happen inside the converter.

Classes

// Custom serializer/deserializer.
// You must implement serialize and deserialize methods
@JsonConverter
class DeviceConverter implements JsonCustomConvert<Device> {

    // We receive the instance and just serialize it with the standard json2typescript method.
    serialize(device: Device): any {
        const jsonConvert: JsonConvert = new JsonConvert();
        return jsonConvert.serialize(device);
    }

    // We receive a json object (not string) and decide 
    // based on the given properties whether we want to 
    // create an instance of Computer or Mobile.
    deserialize(device: any): Device {

        const jsonConvert: JsonConvert = new JsonConvert();

        // We need the try/catch because of deserialize inside
        try {
        if (device.name && device.macAddress) {
                const computer: Computer = new Computer();
                computer.name = device.name;
                computer.macAddress = device.macAddress;
                return jsonConvert.deserialize(computer, Computer);
            } else if (device.name && device.number) 
                const mobile: Mobile = new Mobile();
                mobile.name = device.name;
                mobile.number = device.number;
                return jsonConvert.deserialize(mobile, Mobile);
            }
        } catch(e) {}

        throw new TypeError();

    }

} 

@JsonObject
class JsonDevices {
    @JsonProperty("devices", DeviceConverter)
    devices: Device[] = [];
}

@JsonObject
class Device {
    @JsonProperty("name", String)
    name: string = undefined;
}

@JsonObject
class Mobile extends Device {
    @JsonProperty("number", String)
    number: string = undefined;
}

@JsonObject
class Computer extends Device {
    @JsonProperty("macAddress", String)
    macAddress: string = undefined;
}

Usage

// Assume this is your incoming json
const jsonString: string = ("
    [{
        'name':'mobile1',
        'number':'600 600 600',
        'class':'Mobile'
    },{
        'name':'computer',
        'macAddress:'123123123',
        'class':'Computer'
    }]
");

// Convert it to an JSON/JavaScript object
// In the current Angular 4 HttpClientModule API, 
// you would get an object directly and you wouldn't 
// bother with it anyway.
const jsonArray: any[] = JSON.parse(jsonString);

// Make sure the given array is added to an object 
// having a device array
const jsonDevicesObject: any = {
    devices: jsonArray;
}

// Now deserialize the whole thing with json2typescript:
const jsonConvert: JsonConvert = new JsonConvert();
const jsonDevices: JsonDevices = jsonConvert.deserialize(jsonDevicesObject, JsonDevices);

// Now all elements of jsonDevices.devices are of instance Mobile or Computer

Upvotes: 1

Related Questions