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

时间:2014-04-05 01:46:18

标签: json typescript

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

我不想填充typescript var(即有一个带有这个JSON对象的构造函数)。它很大并且通过子对象和子对象在子对象之间复制所有内容。物业财产需要很长时间。

更新:但您可以cast it to a typescript interface!

25 个答案:

答案 0 :(得分:128)

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

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

复制数据的一些例子:

  1. Copying AJAX JSON object into existing Object
  2. Parse JSON String into a Particular Object Prototype in JavaScript
  3. 从本质上讲,你只是:

    var d = new MyRichObject();
    d.copyInto(jsonResult);
    

答案 1 :(得分:70)

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

它的工作原理如下:

        let jsonObject = response.json() as Object;
        let fooInstance = plainToClass(Models.Foo, jsonObject);
        return fooInstance;

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

答案 2 :(得分:41)

在TypeScript中,您可以使用界面和泛型来执行type assertion,如下所示:

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

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

我希望有所帮助!

答案 3 :(得分:29)

如果您使用的是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()
})

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

答案 4 :(得分:25)

我发现了一篇关于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

答案 5 :(得分:16)

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

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

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

   // call the methods on your newCompany object ...
}

为了完成这项工作:

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

2)注意您必须对链接对象执行相同操作。对于示例中的Employees,您还要创建一个构造函数,为员工提供json数据的一部分。您调用$ .map将json员工转换为打字稿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);
    }
}

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

答案 6 :(得分:15)

TLDR:一线

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

详细答案

推荐使用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];
      }
    }
  }
}

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

以下是如何在http resp ...

之类的内容中使用它
this.http.get(ENDPOINT_URL)
  .map(res => res.json())
  .map((resp: Person) => new Person(resp) ) );

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

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

如果您拥有特定类型的类成员(也就是:另一个类的实例),那么您可以通过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));
  }
}

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

答案 7 :(得分:13)

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

let u:User = new User();
Object.assign(u , jsonUsers);

更高级的使用示例。使用数组的示例。

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

答案 8 :(得分:7)

没有什么可以自动检查从服务器收到的JSON对象是否具有预期的(读取符合)typescript的接口属性。但您可以使用User-Defined Type Guards

考虑以下界面和愚蠢的json对象(它可以是任何类型):

interface MyInterface {
    key: string;
 }

const json: object = { "key": "value" }

三种可能的方式:

一个。在变量

之后键入Assertion或简单静态强制转换
const myObject: MyInterface = json as MyInterface;

B中。简单的静态演员,变量之前和钻石之间

const myObject: MyInterface = <MyInterface>json;

℃。高级动态强制转换,你自己检查对象的结构

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}'.`);
}

你可以play with this example here

请注意,这里的难点是编写isMyInterface函数。我希望TS迟早会向export complex typing添加一个装饰器到运行时,让运行时在需要时检查对象的结构。现在,您可以使用json模式验证器,其目的大致相同或者runtime type check function generator

答案 9 :(得分:6)

使用'as'声明:

const data = JSON.parse(response.data) as MyClass;

答案 10 :(得分:3)

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

Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)

答案 11 :(得分:2)

虽然不按照说法投票;我发现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"

答案 12 :(得分:2)

一个老问题,答案大多正确但不是很有效。这就是我的建议:

创建一个包含 init()方法和静态强制转换方法(对于单个对象和数组)的基类。静态方法可以在任何地方;具有基类和 init()的版本允许以后轻松扩展。

import { ContentItem } from './content-item';

export class SubjectArea extends ContentItem {
    id: number;
    title: string;
    areas: SubjectArea[]; // contains embedded objects
    depth: number;

    // method will be unavailable unless we use cast
    lead(): string {
        return '. '.repeat(this.depth);
    }

    // in case we have embedded objects, call cast on them here
    init() {
        if (this.areas) {
            this.areas = ContentItem.castAllAs(this.areas, SubjectArea);
        }
    }
}

在@ Adam111p帖子中提到了类似的机制(使用 assign())。只是另一种(更完整)的方式来做到这一点。 @Timothy Perez对 assign()持批评态度,但imho在这里完全合适。

实现派生(真实)类:

const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);

现在我们可以转换从服务中检索的对象:

export abstract class BaseService<T extends ContentItem> {
  BASE_URL = 'http://host:port/';
  protected abstract http: Http;
  abstract path: string;
  abstract subClass: typeof ContentItem;

  cast(source: T): T {
    return ContentItem.castAs(source, this.subClass);
  }
  castAll(source: T[]): T[] {
    return ContentItem.castAllAs(source, this.subClass);
  }

  constructor() { }

  get(): Promise<T[]> {
    const value = this.http.get(`${this.BASE_URL}${this.path}`)
      .toPromise()
      .then(response => {
        const items: T[] = this.castAll(response.json());
        return items;
      });
    return value;
  }
}

SubjectArea 对象的所有层次结构都具有正确的类。

用例/示例;创建一个Angular服务(再次抽象基类):

@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
  path = 'area';
  subClass = SubjectArea;

  constructor(protected http: Http) { super(); }
}

用法变得非常简单;创建区域服务:

export class OtherItem extends ContentItem {...}
该服务的

get()方法将返回已作为 SubjectArea 对象(整个层次结构)转换的数组的Promise

现在说,我们还有另一个班级:

@Injectable()
export class OtherItemService extends BaseService<OtherItem> {
  path = 'other';
  subClass = OtherItem;

  constructor(protected http: Http) { super(); }
}

创建一个检索数据并转换为正确类的服务非常简单:

<script>

function searchVideo() {
    var separator = ',';
    $.getJSON('https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&pageToken=' + pageToken + '&playlistId=PLrEnWoR732-BHrPp_Pm8_VleD68f9s14-&key=Apikey&callback=?',function(data){

        var l = data.items.length;
        pageToken = data.nextPageToken;
        numOfResult += l;
        var itemUrl = '';
        var videoids = [];
        for(var j = 0; j < l; j++) {
            if( j == 0) {
                separator = ',';
            } else {
                separator = ',';
            }
            var videoid = data.items[j].snippet.resourceId.videoId;
            var title = data.items[j].snippet.title;
            $.ajax({
                method: 'POST',
                url:    'add.php',
                data: { title: title, videoid: videoid }
            }).done(function(data) {
            });
        }

        if( numOfResult < maxResults) {
          searchVideo();

        }

    });
}

var data_length =  2;
var pageToken = ''; 
var numOfResult = 0;
var maxResults = 200;
for(var i=0; i<data_length; i++){
    console.log(i);

    searchVideo();
}



  </script>

答案 13 :(得分:2)

https://jvilk.com/MakeTypes/

您可以使用此站点为您生成代理。它会生成一个类,并且可以解析和验证您输入的JSON对象。

答案 14 :(得分:1)

您可以将json转换为这样的属性

class Jobs {
  constructor(JSONdata) {
    this.HEAT = JSONdata.HEAT;    
    this.HEAT_EAF = JSONdata.HEAT_EAF;    
  }
  
}

 var job = new Jobs({HEAT:'123',HEAT_EAF:'456'});

答案 15 :(得分:1)

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

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

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

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

interface IPerson {
    name: string;
    address: IAddress;
}

如果我们以面向对象的术语来考虑以上内容,则上述接口不是类,因为它们仅定义了数据结构。 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;
    }
}

现在我们可以简单地传入任何符合IPerson结构的对象,然后就开始了……

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

我们现在可以用相同的方式处理端点上接收到的对象,这些对象类似于...

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

person.distanceFrom({ street: "Some street address", etc.});

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

答案 16 :(得分:0)

我遇到了类似的需求。 我想要一些可以使我轻松地从JSON转换到JSON的东西 来自对特定类定义的REST api调用。 我发现的解决方案不足或打算重写我的解决方案 类的代码,并添加注释或类似内容。

我希望在Java中使用GSON之类的东西来将类与JSON对象进行序列化/反序列化。

结合以后的需要,该转换器也将在JS中运行,我结束了编写自己的程序包。

尽管有一些开销。但是启动时,添加和编辑非常方便。

您可以使用初始化模块: 1.转换模式-允许在字段之间进行映射并确定 转换将如何完成 2.类映射数组 3.转换功能图-用于特殊转换。

然后在您的代码中,使用初始化的模块,如:

const convertedNewClassesArray : MyClass[] = this.converter.convert<MyClass>(jsonObjArray, 'MyClass');

const convertedNewClass : MyClass = this.converter.convertOneObject<MyClass>(jsonObj, 'MyClass');

或,转换为JSON:

const jsonObject = this.converter.convertToJson(myClassInstance);

使用指向npm软件包的链接,以及有关如何使用该模块的详细说明:json-class-converter

还包裹着
角度使用:  angular-json-class-converter

答案 17 :(得分:0)

使用从接口扩展的类。

然后:

Object.assign(
                new ToWhat(),
                what
              )

最好:

Object.assign(
                    new ToWhat(),
                    <IDataInterface>what
                  )

ToWhat成为DataInterface的控制器。

答案 18 :(得分:0)

您可以使用此npm软件包。 https://www.npmjs.com/package/class-converter

易于使用,例如:

class UserModel {
  @property('i')
  id: number;

  @property('n')
  name: string;
}

const userRaw = {
  i: 1234,
  n: 'name',
};

// use toClass to convert plain object to class
const userModel = toClass(userRaw, UserModel);
// you will get a class, just like below one
// const userModel = {
//   id: 1234,
//   name: 'name',
// }

答案 19 :(得分:0)

我在前端使用Angular 6,在后端使用Spring Boot应用程序,该应用程序返回Java对象。我需要做的就是在角度应用程序上定义一个具有匹配属性的类似类,然后我可以接受对象“作为”角度类对象(在下例中为 comp as Company )。

例如,请参阅下面的前端代码。让我在评论中知道是否需要进一步说明。

  createCompany() {
    let company = new Company();
    company.name = this.companyName;

    this.companyService.createCompany(company).subscribe(comp => {
       if (comp !== null) {
        this.employerAdminCompany = comp as Company;
      }
    });
  }

其中company是spring boot应用程序中的实体对象,也是Angular中的类。

export class Company {
    public id: number;
    public name: string;
    public owner: User;
    public locations: Array<Location>;
}

答案 20 :(得分:0)

您可以创建自己类型(interface)的SomeType并在其中投射对象。

const typedObject: SomeType = <SomeType> responseObject;

答案 21 :(得分:0)

在后期TS中,您可以这样:

const isMyInterface = (val: any): val is MyInterface => {
  if (!val) { return false; }
  if (!val.myProp) { return false; }
  return true;
};

而且比这样的用户:

if (isMyInterface(data)) {
 // now data will be type of MyInterface
}

答案 22 :(得分:0)

我在这里使用了这个库:https://github.com/pleerock/class-transformer

<script lang="ts">
    import { plainToClass } from 'class-transformer';
</script>

实施:

private async getClassTypeValue() {
  const value = await plainToClass(ProductNewsItem, JSON.parse(response.data));
}

有时候,您必须为plainToClass解析JSON值,以了解它是JSON格式的数据

答案 23 :(得分:-2)

将对象原样传递给类构造函数;没有约定或支票

interface iPerson {
   name: string;
   age: number;
}

class Person {
   constructor(private person: iPerson) { }

   toString(): string {
      return this.person.name + ' is ' + this.person.age;
   }  
}


// runs this as // 
const object1 = { name: 'Watson1', age: 64 };
const object2 = { name: 'Watson2' };            // age is missing

const person1 = new Person(object1);
const person2 = new Person(object2 as iPerson); // now matches constructor

console.log(person1.toString())  // Watson1 is 64
console.log(person2.toString())  // Watson2 is undefined

答案 24 :(得分:-2)

这是一个简单而且非常好的选择

let person = "{"name":"Sam","Age":"30"}";

const jsonParse: ((key: string, value: any) => any) | undefined = undefined;
let objectConverted = JSON.parse(textValue, jsonParse);

然后你会有

objectConverted.name