我从远程REST服务器读取了一个JSON对象。此JSON对象具有typescript类的所有属性(按设计)。如何将收到的JSON对象转换为类型var?
我不想填充typescript var(即有一个带有这个JSON对象的构造函数)。它很大并且通过子对象和子对象在子对象之间复制所有内容。物业财产需要很长时间。
答案 0 :(得分:128)
您不能简单地将来自Ajax请求的普通JavaScript结果转换为原型JavaScript / TypeScript类实例。有许多技术可以做到这一点,通常涉及复制数据。除非您创建类的实例,否则它不会有任何方法或属性。它仍然是一个简单的JavaScript对象。
虽然如果你只处理数据,你可以只对界面进行强制转换(因为它纯粹是一个编译时结构),这需要你使用一个使用数据实例的TypeScript类,使用该数据执行操作。
复制数据的一些例子:
从本质上讲,你只是:
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)
您可以使用此站点为您生成代理。它会生成一个类,并且可以解析和验证您输入的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