简体   繁体   English

如何将 JSON 对象解析为 TypeScript 对象

[英]How to parse a JSON object to a TypeScript Object

I am currently trying to convert my received JSON Object into a TypeScript class with the same attributes and I cannot get it to work.我目前正在尝试将收到的 JSON 对象转换为具有相同属性的 TypeScript 类,但我无法让它工作。 What am I doing wrong?我究竟做错了什么?

Employee Class员工类

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

Employee String员工字符串

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

My Attempt我的尝试

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);

Link to Typescript Playground 链接到 Typescript Playground

If you use a TypeScript interface instead of a class , things are simpler:如果你使用TypeScript接口而不是class ,事情就更简单了:

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: any = JSON.parse(employeeString); // string to generic object first
let employee: Employee = <Employee>jsonObj;

If you want a class , however, simple casting won't work.但是,如果您想要一个class ,那么简单的转换将不起作用。 For example:例如:

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

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

For a class, you'll have to write a constructor which accepts a JSON string/object and then iterate through the properties to assign each member manually, like this:对于一个类,您必须编写一个接受 JSON 字符串/对象的构造函数,然后遍历属性以手动分配每个成员,如下所示:

class Foo {
    name: string;

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

let fObj: Foo = new Foo(theJsonString);

The reason that the compiler lets you cast the object returned from JSON.parse to a class is because typescript is based on structural subtyping .编译器允许您将从JSON.parse返回的对象转换为类的原因是因为typescript 基于结构子类型
You don't really have an instance of an Employee , you have an object (as you see in the console) which has the same properties.您实际上并没有Employee的实例,而是有一个具有相同属性的对象(如您在控制台中看到的)。

A simpler example:一个更简单的例子:

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);

( code in playground ) 操场上的代码

There's no error because a1 satisfies type A because it has all of its properties, and the logA function can be called with no runtime errors even if what it receives isn't an instance of A as long as it has the same properties.没有错误,因为a1满足类型A因为它具有所有属性,并且logA函数可以在没有运行时错误的情况下调用,即使它接收到的不是A的实例,只要它具有相同的属性。

That works great when your classes are simple data objects and have no methods, but once you introduce methods then things tend to break:当你的类是简单的数据对象并且没有方法时,这很有效,但是一旦你引入了方法,事情往往会中断:

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

( code in playground ) 操场上的代码


Edit编辑

This works just fine:这工作得很好:

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);

( code in playground ) 操场上的代码

If you're trying to use JSON.parse on your object when it's not a string:如果您在对象不是字符串时尝试在对象上使用JSON.parse

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);

Then you'll get the error because it's not a string, it's an object, and if you already have it in this form then there's no need to use JSON.parse .然后你会得到错误,因为它不是一个字符串,它是一个对象,如果你已经以这种形式拥有它,那么就不需要使用JSON.parse

But, as I wrote, if you're going with this way then you won't have an instance of the class, just an object that has the same properties as the class members.但是,正如我所写的,如果您采用这种方式,那么您将没有该类的实例,而只是一个与类成员具有相同属性的对象。

If you want an instance then:如果你想要一个实例,那么:

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>"
});
let employee = <Employee>JSON.parse(employeeString);

记住:强类型只是编译时间,因为 javascript 不支持它。

Your JSON data may have some properties that you do not have in your class.您的 JSON 数据可能具有您的类中没有的一些属性。 For mapping You can do simple custom mapping对于映射您可以进行简单的自定义映射

export class Employe{ ////
    static parse(json: string) {
           var data = JSON.parse(json);
            return new Employe(data.typeOfEmployee_id, data.firstName.. and others);
       }
}

and also specifying constructor in your Employee class.并在您的Employee类中指定构造函数。

i like to use a littly tiny library called class-transformer .我喜欢使用一个名为class-transformer的小库。

it can handle nested-objects, map strings to date-objects and handle different json-property-names a lot more.它可以处理嵌套对象,将字符串映射到日期对象并处理更多不同的 json-property-names。

Maybe worth a look.也许值得一看。

import { Type, plainToClass, Expose } from "class-transformer";
import 'reflect-metadata';

export class Employee{
    @Expose({ name: "uid" })
    id: number;

    firstname: string;
    lastname: string;
    birthdate: Date;
    maxWorkHours: number;
    department: string;

    @Type(() => Permission)
    permissions: Permission[] = [];
    typeOfEmployee: string;
    note: string;

    @Type(() => Date)
    lastUpdate: Date;
}

export class Permission {
  type : string;
}

let json:string = {
    "uid": 123,
    "department": "<anystring>",
    "typeOfEmployee": "<anystring>",
    "firstname": "<anystring>",
    "lastname": "<anystring>",
    "birthdate": "<anydate>",
    "maxWorkHours": 1,
    "username": "<anystring>",
    "permissions": [
      {'type' : 'read'},
      {'type' : 'write'}
    ],
    "lastUpdate": "2020-05-08"
}

console.log(plainToClass(Employee, json));

```

First of all you need to be sure that all attributes of that comes from the service are named the same in your class.首先,您需要确保来自服务的所有属性在您的类中命名相同。 Then you can parse the object and after that assign it to your new variable, something like this:然后您可以解析该对象,然后将其分配给您的新变量,如下所示:

const parsedJSON = JSON.parse(serverResponse);
const employeeObj: Employee = parsedJSON as Employee;

Try that!试试看!

Try to use constructor procedure in your class.尝试在您的类中使用构造函数过程。

Object.assign对象.assign

is a key是一把钥匙

Please take a look on this sample:请看一下这个样本:

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

    constructor(original: Object) { 
        Object.assign(this, original);
    }
}

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

You can cast the the json as follows:您可以按如下方式转换 json:

Given your class:鉴于您的课程:

export class Employee{
    firstname: string= '';
}

and the json:和json:

let jsonObj = {
    "firstname": "Hesham"
};

You can cast it as follows:您可以按如下方式进行转换:

let e: Employee = jsonObj as Employee;

And the output of console.log(e);以及console.log(e); is:是:

{ firstname: 'Hesham' } {名字:'Hesham'}

you can make a new object of your class and then assign it's parameters dynamically from the JSON object's parameters.您可以为您的类创建一个新对象,然后从 JSON 对象的参数动态分配它的参数。

const employeeData = JSON.parse(employeeString);
let emp:Employee=new Employee();
const keys=Object.keys(employeeData);
keys.forEach(key=>{
    emp[key]=employeeData[key];
});
console.log(emp);

now the emp is an object of Employee containing all fields of employeeString's Json object(employeeData);现在emp是Employee的一个对象,包含employeeString的Json对象(employeeData)的所有字段;

You can perform this operation using syntax "as"?您可以使用语法“as”执行此操作吗?

async getProfile(): Promise<Contact> {
      const url: string = this.baseApi;
    
      const response = await this.http.get(url).toPromise()
      return JSON.parse(response.json()) as Contact;
    }
if it is coming from server as object you can do 

this.service.subscribe(data:any) 保留任何类型的数据将解决问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM