如何将JSON对象强制转换为typescript类

Dav*_*len 348 json typescript

我从远程REST服务器读取了一个JSON对象.此JSON对象具有typescript类的所有属性(按设计).如何将收到的JSON对象强制转换为类型var?

我不想填充一个typescript var(即有一个带有这个JSON对象的构造函数).它很大并且通过属性按子对象和属性跨子对象复制所有内容将花费大量时间.

更新:您可以将其转换为打字稿界面!

Wir*_*rie 146

您不能简单地将Ajax请求中的普通JavaScript结果转换为原型JavaScript/TypeScript类实例.有许多技术可以实现,通常涉及复制数据.除非您创建类的实例,否则它将不具有任何方法或属性.它仍然是一个简单的JavaScript对象.

如果你只处理数据,你可以只对界面进行强制转换(因为它纯粹是一个编译时结构),这需要你使用一个TypeScript类,它使用数据实例并对该数据执行操作.

复制数据的一些示例:

  1. 将AJAX JSON对象复制到现有Object中
  2. 在JavaScript中将JSON字符串解析为特定对象原型

从本质上讲,你只是:

var d = new MyRichObject();
d.copyInto(jsonResult);
Run Code Online (Sandbox Code Playgroud)

  • 我刚刚在做`Object.assign(new ApiAuthData(), JSON.parse(rawData))` (3认同)
  • 绝对不是我正在寻找的答案:(出于好奇,这是为什么?在我看来,javascript 的工作方式应该是可行的。 (2认同)

Pak*_*Pak 81

我有同样的问题,我找到了一个完成这项工作的库:https://github.com/pleerock/class-transformer.

它的工作原理如下:

        let jsonObject = response.json() as Object;
        let fooInstance = plainToClass(Models.Foo, jsonObject);
        return fooInstance;
Run Code Online (Sandbox Code Playgroud)

它支持嵌套的子级,但您必须装饰类的成员.

  • 这个精彩的小库以最少的努力完美地解决了它(不要忘记你的`@ Type`注释).这个答案值得更多的信任. (11认同)
  • 请注意,此库几乎不再维护.它不再适用于Angular5 +,因为它们甚至不再合并拉取请求,我认为它们不会很快就能解决这个问题.这是一个很棒的图书馆. (2认同)
  • 这在Angular 6中很好用(至少对于我的用例来说,它只是字面上映射JSON <=>类) (2认同)
  • 使用 angular8+ 并正在积极开发中。对我来说,这是最重要的实用程序库之一 (2认同)

use*_*310 49

在TypeScript中,您可以使用接口和泛型来执行类型断言,如下所示:

var json = Utilities.JSONLoader.loadFromFile("../docs/location_map.json");
var locations: Array<ILocationMap> = JSON.parse(json).location;
Run Code Online (Sandbox Code Playgroud)

ILocationMap描述数据的形状.此方法的优点是您的JSON可以包含更多属性,但形状满足接口的条件.

我希望有所帮助!

  • 仅供参考:这是一种类型断言,而不是演员. (45认同)
  • 但它没有任何方法,如答案所述. (21认同)
  • 我在哪里可以找到Utilities.JSONLoader? (7认同)
  • 有关_type assertion_和_cast_之间的区别,请参见[here](http://basarat.gitbooks.io/typescript/content/docs/types/type-assertion.html). (5认同)
  • 要点是能够使用在类型中实现的方法。 (2认同)

ilo*_*zcd 32

如果您使用的是ES6,请尝试以下方法:

class Client{
  name: string

  displayName(){
    console.log(this.name)
  }
}

service.getClientFromAPI().then(clientData => {

  // Here the client data from API only have the "name" field
  // If we want to use the Client class methods on this data object we need to:
  let clientWithType = Object.assign(new Client(), clientData)

  clientWithType.displayName()
})
Run Code Online (Sandbox Code Playgroud)

但遗憾的是,这种方式对nest对象不起作用.

  • 他们在打字稿中要求它。 (3认同)
  • 如果缺少默认构造函数,可以通过“Object.create(MyClass.prototype)”创建目标实例,完全绕过构造函数。 (2认同)
  • 有关嵌套对象限制的更多说明,请参阅/sf/ask/1602019681/#comment70138082_39423567 (2认同)

Phi*_*nci 27

我发现了一篇关于JSON到Typescript类的通用转换的非常有趣的文章:

http://cloudmark.github.io/Json-Mapping/

您最终得到以下代码:

let example = {
                "name": "Mark", 
                "surname": "Galea", 
                "age": 30, 
                "address": {
                  "first-line": "Some where", 
                  "second-line": "Over Here",
                  "city": "In This City"
                }
              };

MapUtils.deserialize(Person, example);  // custom class
Run Code Online (Sandbox Code Playgroud)


Tim*_*rez 17

TLDR:一个班轮

// This assumes your constructor method will assign properties from the arg.
.map((instanceData: MyClass) => new MyClass(instanceData));
Run Code Online (Sandbox Code Playgroud)

详细答案

推荐使用Object.assign方法,因为它可能会不恰当地丢弃您的类实例与不相关的属性(以及定义的闭包),这些属性未在类本身中声明.

在您尝试反序列化的类中,我将确保定义要反序列化的任何属性(null,空数组等).通过使用初始值定义属性,可以在尝试迭代类成员以指定值时公开其可见性(请参阅下面的反序列化方法).

export class Person {
  public name: string = null;
  public favoriteSites: string[] = [];

  private age: number = null;
  private id: number = null;
  private active: boolean;

  constructor(instanceData?: Person) {
    if (instanceData) {
      this.deserialize(instanceData);
    }
  }

  private deserialize(instanceData: Person) {
    // Note this.active will not be listed in keys since it's declared, but not defined
    const keys = Object.keys(this);

    for (const key of keys) {
      if (instanceData.hasOwnProperty(key)) {
        this[key] = instanceData[key];
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我只是创建了一个反序列化方法.在一个现实世界的例子中,我会将它集中在一个可重用的基类或服务方法中.

以下是如何在http resp中使用它...

this.http.get(ENDPOINT_URL)
  .map(res => res.json())
  .map((resp: Person) => new Person(resp) ) );
Run Code Online (Sandbox Code Playgroud)

如果tslint/ide抱怨参数类型不兼容,只需使用尖括号将参数转换为相同的类型<YourClassName>,例如:

const person = new Person(<Person> { name: 'John', age: 35, id: 1 });
Run Code Online (Sandbox Code Playgroud)

如果您具有特定类型的类成员(也就是:另一个类的实例),那么您可以通过getter/setter方法将它们转换为类型化实例.

export class Person {
  private _acct: UserAcct = null;
  private _tasks: Task[] = [];

  // ctor & deserialize methods...

  public get acct(): UserAcct {
    return this.acct;
  }
  public set acct(acctData: UserAcct) {
    this._acct = new UserAcct(acctData);
  }

  public get tasks(): Task[] {
    return this._tasks;
  }

  public set tasks(taskData: Task[]) {
    this._tasks = taskData.map(task => new Task(task));
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的示例将acct和任务列表反序列化为它们各自的类实例.

  • 我喜欢单线;-) (2认同)

Ant*_*ère 16

假设json具有与typescript类相同的属性,则不必将Json属性复制到typescript对象.您只需构造您的Typescript对象,在构造函数中传递json数据.

在您的ajax回调中,您收到了一家公司:

onReceiveCompany( jsonCompany : any ) 
{
   let newCompany = new Company( jsonCompany );

   // call the methods on your newCompany object ...
}
Run Code Online (Sandbox Code Playgroud)

为了使这项工作:

1)在Typescript类中添加一个构造函数,该构造函数将json数据作为参数.在该构造函数中,您使用jQuery扩展json对象,如下所示:$.extend( this, jsonData).$ .extend允许在添加json对象的属性时保留javascript原型.

2)注意您必须对链接对象执行相同操作.对于示例中的Employees,您还要创建一个构造函数,为员工提供json数据的一部分.您调用$ .map将json员工转换为typescript Employee对象.

export class Company
{
    Employees : Employee[];

    constructor( jsonData: any )
    {
        $.extend( this, jsonData);

        if ( jsonData.Employees )
            this.Employees = $.map( jsonData.Employees , (emp) => {
                return new Employee ( emp );  });
    }
}

export class Employee
{
    name: string;
    salary: number;

    constructor( jsonData: any )
    {
        $.extend( this, jsonData);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我在处理Typescript类和json对象时找到的最佳解决方案.

  • 在Angular项目中使用JQuery是一个糟糕的主意.如果你的模型包含很多函数,它们就不再是模型了. (6认同)

Ada*_*11p 14

就我而言,它有效.我使用了函数 Object.assign(target,sources ...).首先,创建正确的对象,然后将数据从json对象复制到target.Example:

let u:User = new User();
Object.assign(u , jsonUsers);
Run Code Online (Sandbox Code Playgroud)

还有一个更高级的使用示例.使用数组的示例.

this.someService.getUsers().then((users: User[]) => {
  this.users = [];
  for (let i in users) {
    let u:User = new User();
    Object.assign(u , users[i]);
    this.users[i] = u;
    console.log("user:" + this.users[i].id);
    console.log("user id from function(test it work) :" + this.users[i].getId());
  }

});

export class User {
  id:number;
  name:string;
  fullname:string;
  email:string;

  public getId(){
    return this.id;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 或者更好地利用返回值:`this.users[i] = Object.assign(new User(), users[i]);` (2认同)

Fla*_*ken 12

没有什么可以自动检查从服务器收到的JSON对象是否具有预期的(读取符合)typescript的接口属性.但您可以使用用户定义的类型保护

考虑以下接口和一个愚蠢的json对象(可能是任何类型):

interface MyInterface {
    key: string;
 }

const json: object = { "key": "value" }
Run Code Online (Sandbox Code Playgroud)

三种可能的方式:

A.在变量后面输入Assertion或简单静态强制转换

const myObject: MyInterface = json as MyInterface;
Run Code Online (Sandbox Code Playgroud)

B.简单的静态铸造,变量之前和钻石之间

const myObject: MyInterface = <MyInterface>json;
Run Code Online (Sandbox Code Playgroud)

C.高级动态演员,你自己检查一下对象的结构

function isMyInterface(json: any): json is MyInterface {
    // silly condition to consider json as conform for MyInterface
    return typeof json.key === "string";
}

if (isMyInterface(json)) {
    console.log(json.key)
}
else {
        throw new Error(`Expected MyInterface, got '${json}'.`);
}
Run Code Online (Sandbox Code Playgroud)

你可以在这里玩这个例子

请注意,这里的难点是编写isMyInterface函数.我希望TS迟早会添加一个装饰器来将复杂类型输出到运行时,让运行时在需要时检查对象的结构.现在,您可以使用json模式验证器,其目的与此运行时类型检查函数生成器大致相同


Nei*_*eil 8

虽然它本身不是铸造;我发现https://github.com/JohnWhiteTB/TypedJSON是一个有用的替代方案。

@JsonObject
class Person {
    @JsonMember
    firstName: string;

    @JsonMember
    lastName: string;

    public getFullname() {
        return this.firstName + " " + this.lastName;
    }
}
var person = TypedJSON.parse('{ "firstName": "John", "lastName": "Doe" }', Person);

person instanceof Person; // true
person.getFullname(); // "John Doe"
Run Code Online (Sandbox Code Playgroud)


Rod*_*ati 6

就我个人而言,我觉得打字稿不允许端点定义指定所接收对象的类型是令人震惊的。看来情况确实如此,我会做我对其他语言所做的事情,即将 JSON 对象与类定义分开,并让类定义使用 JSON 对象作为其唯一的数据成员。

我鄙视样板代码,所以对我来说,通常是在保留类型的同时用最少的代码获得所需的结果。

考虑以下 JSON 对象结构定义 - 这些将是您在端点处收到的内容,它们只是结构定义,没有方法。

interface IAddress {
    street: string;
    city: string;
    state: string;
    zip: string;
}

interface IPerson {
    name: string;
    address: IAddress;
}
Run Code Online (Sandbox Code Playgroud)

如果我们从面向对象的角度思考上述内容,则上述接口不是类,因为它们仅定义了数据结构。OO 术语中的类定义数据和对其进行操作的代码。

所以我们现在定义一个类来指定数据和对其进行操作的代码......

class Person {
    person: IPerson;

    constructor(person: IPerson) {
        this.person = person;
    }

    // accessors
    getName(): string {
        return person.name;
    }

    getAddress(): IAddress {
        return person.address;
    }

    // You could write a generic getter for any value in person, 
    // no matter how deep, by accepting a variable number of string params

    // methods
    distanceFrom(address: IAddress): float {
        // Calculate distance from the passed address to this persons IAddress
        return 0.0;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以简单地传入任何符合 IPerson 结构的对象并开始我们的工作......

   Person person = new Person({
            name: "persons name",
            address: {
                street: "A street address",
                city: "a city",
                state: "a state",
                zip: "A zipcode"
            }
        });
Run Code Online (Sandbox Code Playgroud)

以同样的方式,我们现在可以使用以下方式处理在端点接收到的对象:

Person person = new Person(req.body);    // As in an object received via a POST call

person.distanceFrom({ street: "Some street address", etc.});
Run Code Online (Sandbox Code Playgroud)

这会提高性能,并且使用复制数据的一半内存,同时显着减少必须为每种实体类型编写的样板代码量。它仅依赖于 TypeScript 提供的类型安全性。


小智 6

使用“as”声明:

const data = JSON.parse(response.data) as MyClass;
Run Code Online (Sandbox Code Playgroud)

  • 这种技术在[两年前的答案](/sf/answers/3379773001/)中提到过,并且正如其他地方所讨论的那样,不会添加任何可能在“MyClass”上声明的函数。 (3认同)

Sam*_*Sam 5

使用从接口扩展的类。

然后:

    Object.assign(
        new ToWhat(),
        what
    )
Run Code Online (Sandbox Code Playgroud)

最好的是:

    Object.assign(
        new ToWhat(),
        <IDataInterface>what
    )
Run Code Online (Sandbox Code Playgroud)

ToWhat成为控制者DataInterface


s_b*_*ead 5

如果您需要将 json 对象转换为 Typescript 类,并在生成的对象上使用其实例方法,则需要使用Object.setPrototypeOf,就像我在下面的代码片段中所做的那样:

Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

382191 次

最近记录:

5 年,11 月 前