如何将 JSON 对象转换为 typescript 类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22875636/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I cast a JSON object to a typescript class
提问by David Thielen
I read a JSON object from a remote REST server. This JSON object has all the properties of a typescript class (by design). How do I cast that received JSON object to a type var?
我从远程 REST 服务器读取 JSON 对象。这个 JSON 对象具有 typescript 类的所有属性(按设计)。如何将接收到的 JSON 对象转换为类型 var?
I don't want to populate a typescript var (ie have a constructor that takes this JSON object). It's large and copying everything across sub-object by sub-object & property by property would take a lot of time.
我不想填充打字稿 var(即有一个接受这个 JSON 对象的构造函数)。它很大,按子对象和按属性复制子对象中的所有内容将花费大量时间。
Update:You can however cast it to a typescript interface!
更新:但是,您可以将其转换为打字稿界面!
采纳答案by WiredPrairie
You can't simple cast a plain-old-JavaScript result from an Ajax request into a prototypical JavaScript/TypeScript class instance. There are a number of techniques for doing it, and generally involve copying data. Unless you create an instance of the class, it won't have any methods or properties. It will remain a simple JavaScript object.
您不能简单地将来自 Ajax 请求的普通 JavaScript 结果转换为原型 JavaScript/TypeScript 类实例。有许多技术可以做到这一点,通常涉及复制数据。除非您创建类的实例,否则它不会有任何方法或属性。它将仍然是一个简单的 JavaScript 对象。
While if you only were dealing with data, you could just do a cast to an interface (as it's purely a compile time structure), this would require that you use a TypeScript class which uses the data instance and performs operations with that data.
如果您只处理数据,您可以只对接口进行转换(因为它纯粹是一个编译时结构),这将需要您使用 TypeScript 类,该类使用数据实例并使用该数据执行操作。
Some examples of copying the data:
复制数据的一些示例:
- Copying AJAX JSON object into existing Object
- Parse JSON String into a Particular Object Prototype in JavaScript
In essence, you'd just :
从本质上讲,您只需:
var d = new MyRichObject();
d.copyInto(jsonResult);
回答by Pak
I had the same issue and I have found a library that does the job : https://github.com/pleerock/class-transformer.
我遇到了同样的问题,我找到了一个可以完成这项工作的库:https: //github.com/pleerock/class-transformer。
It works like this :
它是这样工作的:
let jsonObject = response.json() as Object;
let fooInstance = plainToClass(Models.Foo, jsonObject);
return fooInstance;
It supports nested childs but you have to decorate your class's member.
它支持嵌套的孩子,但你必须装饰你的班级成员。
回答by user756310
In TypeScript you can do a type assertionusing an interface and generics like so:
在 TypeScript 中,您可以使用接口和泛型进行类型断言,如下所示:
var json = Utilities.JSONLoader.loadFromFile("../docs/location_map.json");
var locations: Array<ILocationMap> = JSON.parse(json).location;
Where ILocationMap describes the shape of your data. The advantage of this method is that your JSON could contain more properties but the shape satisfies the conditions of the interface.
ILocationMap 描述数据的形状。这种方法的优点是你的JSON可以包含更多的属性,但形状满足接口的条件。
I hope that helps!
我希望这有帮助!
回答by migcoder
If you are using ES6, try this:
如果您使用的是 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()
})
But this way will not work on the nest object, sadly.
但遗憾的是,这种方式不适用于嵌套对象。
回答by Philip
I found a very interesting article on generic casting of JSON to a Typescript Class:
我发现了一篇关于将 JSON 通用转换为 Typescript 类的非常有趣的文章:
http://cloudmark.github.io/Json-Mapping/
http://cloudmark.github.io/Json-Mapping/
You end up with following code:
你最终得到以下代码:
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
回答by Timothy Perez
TLDR: One liner
TLDR:一个班轮
// This assumes your constructor method will assign properties from the arg.
.map((instanceData: MyClass) => new MyClass(instanceData));
The Detailed Answer
详细解答
I would notrecommend the Object.assign approach, as it can inappropriately litter your class instance with irrelevant properties (as well as defined closures) that were not declared within the class itself.
我不推荐 Object.assign 方法,因为它可能会用未在类本身中声明的不相关属性(以及定义的闭包)不恰当地乱扔类实例。
In the class you are trying to deserialize into, I would ensure any properties you want deserialized are defined (null, empty array, etc). By defining your properties with initial values you expose their visibility when trying to iterate class members to assign values to (see deserialize method below).
在您尝试反序列化的类中,我会确保定义了您想要反序列化的任何属性(空、空数组等)。通过使用初始值定义属性,您可以在尝试迭代类成员以为其分配值时公开它们的可见性(请参阅下面的反序列化方法)。
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];
}
}
}
}
In the example above, I simply created a deserialize method. In a real world example, I would have it centralized in a reusable base class or service method.
在上面的示例中,我只是创建了一个反序列化方法。在现实世界的示例中,我会将其集中在可重用的基类或服务方法中。
Here is how to utilize this in something like an http resp...
这是如何在诸如 http 响应之类的东西中使用它...
this.http.get(ENDPOINT_URL)
.map(res => res.json())
.map((resp: Person) => new Person(resp) ) );
If tslint/ide complains about argument type being incompatible, just cast the argument into the same type using angular brackets <YourClassName>, example:
如果 tslint/ide 抱怨参数类型不兼容,只需使用尖括号将参数转换为相同类型<YourClassName>,例如:
const person = new Person(<Person> { name: 'John', age: 35, id: 1 });
If you have class members that are of a specific type (aka: instance of another class), then you can have them casted into typed instances through getter/setter methods.
如果您有特定类型的类成员(又名:另一个类的实例),那么您可以通过 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));
}
}
The above example will deserialize both acct and the list of tasks into their respective class instances.
上面的示例将 acct 和任务列表反序列化为它们各自的类实例。
回答by Flavien Volken
There is nothing yet to automatically check if the JSON object you received from the server has the expected (read is conform to the) typescript's interface properties. But you can use User-Defined Type Guards
还没有什么可以自动检查您从服务器收到的 JSON 对象是否具有预期的(读取符合)打字稿的接口属性。但是你可以使用用户定义的类型保护
Considering the following interface and a silly json object (it could have been any type):
考虑以下接口和一个愚蠢的 json 对象(它可以是任何类型):
interface MyInterface {
key: string;
}
const json: object = { "key": "value" }
Three possible ways:
三种可能的方式:
A. Type Assertion or simple static cast placed after the variable
A. 类型断言或简单的静态转换放在变量之后
const myObject: MyInterface = json as MyInterface;
B. Simple static cast, before the variable and between diamonds
B. 简单的静态转换,在变量之前和菱形之间
const myObject: MyInterface = <MyInterface>json;
C. Advanced dynamic cast, you check yourself the structure of the object
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}'.`);
}
You can play with this example here
你可以在这里玩这个例子
Note that the difficulty here is to write the isMyInterfacefunction. I hope TS will add a decorator sooner or later to export complex typingto the runtime and let the runtime check the object's structure when needed. For now, you could either use a json schema validator which purpose is approximately the same OR this runtime type check function generator
请注意,这里的难点是编写isMyInterface函数。我希望 TS 迟早会添加一个装饰器,将复杂的类型导出到运行时,并让运行时在需要时检查对象的结构。现在,您可以使用目的大致相同的 json 模式验证器或此运行时类型检查函数生成器
回答by Anthony Brenelière
Assuming the json has the same properties as your typescript class, you don't have to copy your Json properties to your typescript object. You will just have to construct your Typescript object passing the json data in the constructor.
假设 json 与您的 typescript 类具有相同的属性,您不必将您的 Json 属性复制到您的 typescript 对象。您只需要构造 Typescript 对象,并在构造函数中传递 json 数据。
In your ajax callback, you receive a company:
在您的 ajax 回调中,您会收到一家公司:
onReceiveCompany( jsonCompany : any )
{
let newCompany = new Company( jsonCompany );
// call the methods on your newCompany object ...
}
In in order to to make that work:
为了使这项工作:
1) Add a constructor in your Typescript class that takes the json data as parameter. In that constructor you extend your json object with jQuery, like this: $.extend( this, jsonData). $.extend allows keeping the javascript prototypes while adding the json object's properties.
1) 在 Typescript 类中添加一个以 json 数据作为参数的构造函数。在该构造你使用jQuery扩展您的JSON对象,像这样:$.extend( this, jsonData)。$.extend 允许在添加 json 对象的属性时保留 javascript 原型。
2) Note you will have to do the same for linked objects. In the case of Employees in the example, you also create a constructor taking the portion of the json data for employees. You call $.map to translate json employees to typescript Employee objects.
2) 请注意,您必须对链接对象执行相同操作。在示例中的员工的情况下,您还创建了一个构造函数,以获取员工的 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);
}
}
This is the best solution I found when dealing with Typescript classes and json objects.
这是我在处理 Typescript 类和 json 对象时找到的最佳解决方案。
回答by Adam111p
In my case it works. I used functions Object.assign (target, sources ...). First, the creation of the correct object, then copies the data from json object to the target.Example :
就我而言,它有效。我使用了函数 Object.assign (target, sources ...)。首先,创建正确的对象,然后将数据从 json 对象复制到 target.Example :
let u:User = new User();
Object.assign(u , jsonUsers);
And a more advanced example of use. An example using the array.
以及更高级的使用示例。使用数组的示例。
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;
}
}
回答by Neil
While it is not casting per se; I have found https://github.com/JohnWhiteTB/TypedJSONto be a useful alternative.
虽然它本身不是铸造;我发现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"

