9

I get from my backend a JSON that looks like this:

[{
    "schedulingId": "7d98a02b-e14f-43e4-a8c9-6763ba6a5e76",
    "schedulingDateTime": "2019-12-28T14:00:00",
    "registrationDateTime": "2019-12-24T16:47:34",
    "doctorViewModel": {
        "doctorId": "a49d9534-65a6-4730-ac45-4dc2f91165e0",
        "doctorName": "Ana"
    },
    "personViewModel": {
        "personId": "3607c475-e287-4e83-85e6-a46f4a0116d6",
        "personName": "João",
        "birthDate": "1970-09-18T00:00:00"
    },
    "consultaViewModel": null
}]

And I need to deserialize that JSON into my Scheduling parsing the date from JSON to 'dd/MM/yyyy hh:MM:ss' format:

export interface Scheduling {
   schedulingId : string;
   schedulingDateTime : Date;
   registrationDateTime : Date;
   person : Person;
   doctor : Doctor;
   consulta? : Consulta;
}

export interface Person {
   personId : string;
   personName : string;
   birthDate : Date;
}

export interface Doctor {
    doctorId : string;
    doctorName : string;
}

export interface Consulta {
    consultaId : string;
    consultaDateTime : Date;
}

But I'm not sure how I can do that after calling my get method:

this.httpClient.get<Scheduling>(`${applicationUrl}/scheduling/${date}`);
Rômulo Borges
  • 127
  • 1
  • 3
  • 9
  • 3
    The problem is that Typescript typings disappear at runtime. Because of this, there is no way for Javascript to know which type you expect from a JSON payload that arrives at runtime. If somebody has an automated way of doing it, I'm interested in knowing it. Otherwise, you'll have to do it manually by wrapping the fields with `new Date(payload.myField)`. – gretro Dec 26 '19 at 17:51
  • Can you convert these interfaces to classes instead? – Nicholas K Dec 26 '19 at 18:42
  • @NicholasK yes, I can – Rômulo Borges Dec 26 '19 at 18:50
  • 1
    `Date` objects do not have formats. They simply record the number of milliseconds since midnight UTC, 1970-01-01, and some metadata. – Heretic Monkey Dec 26 '19 at 19:27
  • Does this answer your question? [Javascript JSON Date Deserialization](https://stackoverflow.com/questions/14488745/javascript-json-date-deserialization) – Heretic Monkey Dec 26 '19 at 19:27

2 Answers2

4

There are two ways to achieve this, using direct mapping and using constructors,

You can achieve this using pipe and map operator from rxjs/operators

1. Direct mapping

import { map } from "rxjs/operators";

this.httpClient.get<Scheduling>(`${applicationUrl}/scheduling/${date}`)
   .pipe(
        map(scheduling => {
          scheduling.schedulingDateTime = new Date(scheduling.schedulingDateTime);
          scheduling.registrationDateTime = new Date(scheduling.registrationDateTime);
          scheduling.personViewModel.birthDate = new Date(scheduling.personViewModel.birthDate);
          return scheduling;
        })
    );

2. Using constructors

Here will pass each received json to constructor of classes and parse the date in constructor.

export class Person {
  personId: string;
  personName: string;
  birthDate: Date;

  constructor(init: Person) {
    this.personId = init.personId;
    this.personName = init.personName;
    this.birthDate = parseDate(init.birthDate);
  }
}

export class Consulta {
  consultaId: string;
  consultaDateTime: Date;

  constructor(init: Consulta) {
    this.consultaId = init.consultaId;
    this.consultaDateTime = parseDate(init.consultaDateTime);
  }
}

export class Doctor {
  doctorId: string;
  doctorName: string;

  constructor(init: Doctor) {
    this.doctorId = init.doctorId;
    this.doctorName = init.doctorName;
  }
}


export class Scheduling {
  schedulingId: string;
  schedulingDateTime: Date;
  registrationDateTime: Date;
  person: Person;
  doctor: Doctor;
  consulta?: Consulta;

  constructor(init: Scheduling) {

    this.schedulingId = init.schedulingId;

    this.schedulingDateTime = parseDate(init.schedulingDateTime);
    this.registrationDateTime = parseDate(init.registrationDateTime);

    this.person = init.person !== undefined ? new Person(init.person) : undefined;
    this.consulta = init.consulta !== undefined ? new Consulta(init.consulta) : undefined;
    this.doctor = init.doctor !== undefined ? new Doctor(init.doctor) : undefined;
  }
}



export function parseDate(str: string | Date): Date {
  if (str !== undefined && str !== null) {
    return new Date(str);
  }
  return undefined;
}

Service

this.http.get<Scheduling[]>(`${applicationUrl}/scheduling/${date}`)
      .pipe(
        map(schedulings => {
          const modifiedSchedulings = []
          for (const scheduling of schedulings) {
            modifiedSchedulings.push(new Scheduling(scheduling));
          }
          return modifiedSchedulings;
        })
      );
Plochie
  • 3,944
  • 1
  • 17
  • 33
1

If you convert the interfaces to classes then you can create constructors for them and return the desired response.

Defined the classes:

export class Scheduling {
  constructor(
    public schedulingId: string,
    public schedulingDateTime: Date,
    public registrationDateTime: Date,
    public person: Person,
    public doctor: Doctor,
    public consulta?: Consulta
  ) {}
}

export class Person {
  constructor(
    public personId: string,
    public personName: string,
    public birthDate: Date
  ) {}
}

export class Doctor {
  constructor(public doctorId: string, public doctorName: string) {}
}

export class Consulta {
  constructor(public consultaId?: string, public consultaDateTime?: Date) {}
}

now you can map the response to the defined classes as below:

this.httpClient.get<Scheduling[]>(`${applicationUrl}/scheduling/${date}`).pipe(
  map((schArr: any[]) => {
    const resp: Scheduling[] = [];
    schArr.forEach(sch => {
      resp.push(
        new Scheduling(
          sch.schedulingId,
          new Date(this.datePipe.transform(sch.schedulingDateTime, this.FORMAT)),
          new Date(this.datePipe.transform(sch.registrationDateTime, this.FORMAT)),
          new Person(
            sch.personViewModel.personId,
            sch.personViewModel.personName,
            new Date(this.datePipe.transform(sch.personViewModel.birthDate, this.FORMAT))
          ),
          new Doctor(sch.doctorViewModel.doctorId, sch.doctorViewModel.doctorName),
          sch.consultaViewModel != null
            ? new Consulta(sch.consultaViewModel.consultaId, sch.consultaViewModel.consultaId)
            : null
        )
      );
    });
    return resp;
  })
);

For readability define a constant called format and inject the DatePipe service:

  constructor(private datePipe: DatePipe, private httpClient: HttpClient) {}
  FORMAT = "yyyy-MM-dd HH:mm:ss";

Working Stackblitz

Nicholas K
  • 15,148
  • 7
  • 31
  • 57
  • I'm getting: "core.js:6014 ERROR TypeError: Cannot read property 'personId' of undefined". I did a "console.log(sch)" right before "return new Scheduling" and the object that I'm getting is fine – Rômulo Borges Dec 26 '19 at 19:30
  • Just found out that my backend isn't giving me a single object but an array of them. I've just started learning Typescript and Angular and don't know how map works. How can I change that map call to map an array of objects? – Rômulo Borges Dec 26 '19 at 20:03
  • Yes. I've edited the answer and even included a working stackblitz. – Nicholas K Dec 27 '19 at 06:04