【问题标题】:How to parse a JSON object to a TypeScript Object如何将 JSON 对象解析为 TypeScript 对象
【发布时间】:2017-03-18 04:40:18
【问题描述】:

我目前正在尝试将收到的 JSON 对象转换为具有相同属性的 TypeScript 类,但我无法让它工作。我做错了什么?

员工类

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

员工字符串

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

我的尝试

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

【问题讨论】:

  • 什么不完全有效?不是在编译吗?如果是这样,错误是什么?
  • 编辑了我的问题。它现在可以工作了,但该对象不被识别为 Employee,只能被识别为 Object。
  • 检查此gist 并在playground 上尝试。 employee 变量的两个属性都可用。

标签: json angular typescript


【解决方案1】:

如果你使用TypeScript interface 而不是,事情就更简单了:

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;

如果你想要一个,但是,简单的转换是行不通的。例如:

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

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

对于一个类,您必须编写一个接受 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);

【讨论】:

  • 这对我来说似乎是合乎逻辑的。我刚刚看到我在源的 JSON 转换器发送了 id,因为那里的员工类有它们。但它应该发送 id 指向的值。我会调整它,而不是像你的例子一样尝试投射它。
  • 我调整了json字符串并更新了我上面的问题。我也实现了您的解决方案,但仍然无法识别为 Employee 并引发类型不匹配错误。
  • @moessi774 我刚刚更新了我的答案,我想我现在完全理解了你的问题。
  • 我暂时不了解接口。因此,如果我只使用我的类进行类型化,那么界面会更智能。谢谢你的回答。
  • 这个很好的答案来自 2016 年。使用 ES6,您可以在对象的函数中使用 Object.assign(this, input) 以避免手动迭代属性。不过,您仍然需要手动处理对象嵌套。
【解决方案2】:

编译器允许你将JSON.parse返回的对象转换为类的原因是因为typescript is based on structural subtyping
您实际上并没有Employee 的实例,您有一个具有相同属性的对象(如您在控制台中看到的)。

一个更简单的例子:

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)

没有错误,因为a1 满足类型A,因为它具有所有属性,并且logA 函数可以在没有运行时错误的情况下调用,即使它接收到的不是A 的实例只要属性相同。

当你的类是简单的数据对象并且没有方法时,这很有效,但是一旦你引入了方法,事情就会崩溃:

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)


编辑

这很好用:

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)

如果您尝试在不是字符串的对象上使用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);

然后你会得到错误,因为它不是一个字符串,它是一个对象,如果你已经有了这种形式,那么就没有必要使用JSON.parse

但是,正如我所写的,如果您采用这种方式,那么您将没有该类的实例,而只是一个与类成员具有相同属性的对象。

如果你想要一个实例,那么:

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

【讨论】:

  • 所以我基本上可以将任何对象字符串转换为对象或接口,如果它具有与对象或接口相同(但不能具有所有)的属性?这对两者都有效。但是,如果我需要方法,我必须使用类而不是接口,而且我只能调用我的类对象的方法,因为它们是通过类的构造函数创建的。正确的?是否可以立即将我的员工类更改为接口,因为我只需要它进行类型化?
  • 但是,如果字符串的属性比对象多,我也可以将对象字符串转换为对象或接口吗?因为在我的情况下这是行不通的。
  • 检查我修改后的答案
  • 我不明白你的意思。请使用此信息更新您的问题,并说明您做了什么以及得到了什么
  • 使用我得到的确切代码:Employee {department: "&lt;anystring&gt;", typeOfEmployee: "&lt;anystring&gt;", firstname: "&lt;anystring&gt;", lastname: "&lt;anystring&gt;", birthdate: "&lt;anydate&gt;"…} 这很好
【解决方案3】:
let employee = <Employee>JSON.parse(employeeString);

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

【讨论】:

  • 你的例子和他的有什么区别?
【解决方案4】:

您的 JSON 数据可能具有您的类中没有的一些属性。对于映射你可以做简单的自定义映射

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

并在您的 Employee 类中指定构造函数。

【讨论】:

  • 这看起来是个不错的解决方案。我用我现在的方式再试几次。如果它仍然不起作用,我会实施你的。
【解决方案5】:

我喜欢使用一个名为class-transformer 的小库。

它可以处理嵌套对象、将字符串映射到日期对象以及处理更多不同的 json-property-names。

也许值得一看。

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

```

【讨论】:

  • 同意。对于具有嵌套对象层次结构的非平凡案例,类转换器是一个有用的工具,它大大减少了以 JSON 格式发送/接收外部数据的应用所需的手动反序列化代码量。
【解决方案6】:

首先,您需要确保来自服务的所有属性在您的类中命名相同。然后您可以解析该对象,然后将其分配给您的新变量,如下所示:

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

试试看!

【讨论】:

  • 这个方案有个大问题——不支持嵌套对象
【解决方案7】:

尝试在你的类中使用构造函数。

Object.assign

是一把钥匙

请看一下这个样本:

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

【讨论】:

    【解决方案8】:

    您可以按如下方式转换 json:

    鉴于你的班级:

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

    和json:

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

    你可以按如下方式投射:

    let e: Employee = jsonObj as Employee;
    

    console.log(e);的输出是:

    { 名字:'Hesham' }

    【讨论】:

      【解决方案9】:

      您可以为您的类创建一个新对象,然后根据 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);
      

      现在emp是Employee的一个对象,包含employeeString的Json对象(employeeData)的所有字段;

      【讨论】:

        【解决方案10】:
        if it is coming from server as object you can do 
        

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

        【讨论】:

          猜你喜欢
          • 2020-05-02
          • 2021-05-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多