Búsqueda de sitios web

angular - Cómo analizar un objeto JSON a un objeto TypeScript


Actualmente estoy tratando de convertir mi objeto JSON recibido en una clase de TypeScript con los mismos atributos y no puedo hacerlo funcionar. ¿Qué estoy haciendo mal?

Clase de Empleado

export class Employee{
    firstname: string;
    lastname: string;
    birthdate: Date;
    maxWorkHours: number;
    department: string;
    permissions: string;
    typeOfEmployee: string;
    note: string;
    lastUpdate: Date;
}

Cadena de empleado

{
    "department": "<anystring>",
    "typeOfEmployee": "<anystring>",
    "firstname": "<anystring>",
    "lastname": "<anystring>",
    "birthdate": "<anydate>",
    "maxWorkHours": <anynumber>,
    "username": "<anystring>",
    "permissions": "<anystring>",
    "lastUpdate": "<anydate>"
    //I will add note later
}

Mi Intento

let e: Employee = new Employee();

Object.assign(e, {
    "department": "<anystring>",
    "typeOfEmployee": "<anystring>",
    "firstname": "<anystring>",
    "lastname": "<anystring>",
    "birthdate": "<anydate>",
    "maxWorkHours": 3,
    "username": "<anystring>",
    "permissions": "<anystring>",
    "lastUpdate": "<anydate>"
});

console.log(e);

Enlace al Playground de Typescript

Respuesta #1

Si utilizas una interface de TypeScript en lugar de una class, las cosas son más simples:

export interface Employee {
    typeOfEmployee_id: number;
    department_id: number;
    permissions_id: number;
    maxWorkHours: number;
    employee_id: number;
    firstname: string;
    lastname: string;
    username: string;
    birthdate: Date;
    lastUpdate: Date;
}

let jsonObj = JSON.parse(employeeString); // string to "any" object first
let employee = jsonObj as Employee;

Sin embargo, si quieres una clase, la conversión simple no funcionará. Por ejemplo:

class Foo {
    name: string;
    public pump() { }
}

let jsonObj = JSON.parse('{ "name":"hello" }');
let fObj = jsonObj as Foo;
fObj.pump(); // crash, method is undefined!

Para una clase, tendrás que escribir un constructor que acepte una cadena/objeto JSON y luego iterar a través de las propiedades para asignar cada miembro manualmente, así:

class Foo {
    name: string;

    constructor(jsonStr: string) {
        let jsonObj = JSON.parse(jsonStr);
        for (let prop in jsonObj) {
            this[prop] = jsonObj[prop];
        }
    }
}

let fObj = new Foo(theJsonString);

Respuesta #2

La razón por la que el compilador te permite convertir el objeto devuelto por JSON.parse a una clase es porque TypeScript se basa en el subtipado estructural.
Realmente no tienes una instancia de un Empleado, tienes un objeto (como ves en la consola) que tiene las mismas propiedades.

Un ejemplo más simple:

class A {
    constructor(public str: string, public num: number) {}
}

function logA(a: A) {
    console.log(`A instance with str: "${ a.str }" and num: ${ a.num }`);
}

let a1 = { str: "string", num: 0, boo: true };
let a2 = new A("stirng", 0);
logA(a1); // no errors
logA(a2);

(código en el área de juegos)

No hay error porque a1 satisface el tipo A porque tiene todas sus propiedades, y la función logA se puede llamar sin errores en tiempo de ejecución incluso si lo que recibe no es una instancia de A siempre que tenga las mismas propiedades.

Eso funciona muy bien cuando tus clases son simples objetos de datos y no tienen métodos, pero una vez que introduces métodos, las cosas tienden a romperse:

class A {
    constructor(public str: string, public num: number) { }

    multiplyBy(x: number): number {
        return this.num * x;
    }
}

// this won't compile:
let a1 = { str: "string", num: 0, boo: true } as A; // Error: Type '{ str: string; num: number; boo: boolean; }' cannot be converted to type 'A'

// but this will:
let a2 = { str: "string", num: 0 } as A;

// and then you get a runtime error:
a2.multiplyBy(4); // Error: Uncaught TypeError: a2.multiplyBy is not a function

(código en el área de juegos)

Editar

Esto funciona perfectamente:

const employeeString = '{"department":"<anystring>","typeOfEmployee":"<anystring>","firstname":"<anystring>","lastname":"<anystring>","birthdate":"<anydate>","maxWorkHours":0,"username":"<anystring>","permissions":"<anystring>","lastUpdate":"<anydate>"}';
let employee1 = JSON.parse(employeeString);
console.log(employee1);

(código en el área de juegos)

Si estás intentando usar JSON.parse en tu objeto cuando no es una cadena:

let e = {
    "department": "<anystring>",
    "typeOfEmployee": "<anystring>",
    "firstname": "<anystring>",
    "lastname": "<anystring>",
    "birthdate": "<anydate>",
    "maxWorkHours": 3,
    "username": "<anystring>",
    "permissions": "<anystring>",
    "lastUpdate": "<anydate>"
}
let employee2 = JSON.parse(e);

Entonces obtendrás el error porque no es una cadena, es un objeto, y si ya lo tienes en esta forma, entonces no hay necesidad de usar JSON.parse.

Pero, como escribí, si vas por este camino entonces no tendrás una instancia de la clase, solo un objeto que tiene las mismas propiedades que los miembros de la clase.

Si quieres una instancia entonces:

let e = new Employee();
Object.assign(e, {
    "department": "<anystring>",
    "typeOfEmployee": "<anystring>",
    "firstname": "<anystring>",
    "lastname": "<anystring>",
    "birthdate": "<anydate>",
    "maxWorkHours": 3,
    "username": "<anystring>",
    "permissions": "<anystring>",
    "lastUpdate": "<anydate>"
});

Artículos relacionados