원격 REST 서버에서 JSON 객체를 읽습니다. 이 JSON 객체에는 디자인에 따라 유형 스크립트 클래스의 모든 속성이 있습니다. 수신 된 JSON 객체를 유형 var로 캐스트하는 방법
타입 스크립트 var를 채우고 싶지 않습니다 (즉,이 JSON 객체를 취하는 생성자가 있습니다). 크기가 크며 하위 개체 및 속성별로 속성을 복사하는 데 많은 시간이 걸립니다.
업데이트 : 그러나 타이프 스크립트 인터페이스로 캐스트 할 수 있습니다 !
원격 REST 서버에서 JSON 객체를 읽습니다. 이 JSON 객체에는 디자인에 따라 유형 스크립트 클래스의 모든 속성이 있습니다. 수신 된 JSON 객체를 유형 var로 캐스트하는 방법
타입 스크립트 var를 채우고 싶지 않습니다 (즉,이 JSON 객체를 취하는 생성자가 있습니다). 크기가 크며 하위 개체 및 속성별로 속성을 복사하는 데 많은 시간이 걸립니다.
업데이트 : 그러나 타이프 스크립트 인터페이스로 캐스트 할 수 있습니다 !
답변:
Ajax 요청의 일반 오래된 JavaScript 결과를 프로토 타입 JavaScript / TypeScript 클래스 인스턴스로 간단히 캐스트 할 수 없습니다. 이를 수행하는 데는 여러 가지 기술이 있으며 일반적으로 데이터 복사가 필요합니다. 클래스의 인스턴스를 만들지 않으면 메서드 나 속성이 없습니다. 간단한 JavaScript 객체로 유지됩니다.
데이터 만 다루는 경우 인터페이스에 캐스트를 수행 할 수 있지만 (순전히 컴파일 시간 구조이므로) 데이터 인스턴스를 사용하고 해당 데이터에 대한 작업을 수행하는 TypeScript 클래스를 사용해야합니다.
데이터 복사의 몇 가지 예 :
본질적으로, 당신은 단지 :
var d = new MyRichObject();
d.copyInto(jsonResult);
JSON.parse()
. 둘 다 여전히 수행해야하지만 구문 적으로 결합 할 수 있습니다.
Object.setPrototypeOf
나는 같은 문제가 있었고 https://github.com/pleerock/class-transformer 작업을 수행하는 라이브러리를 찾았습니다 .
다음과 같이 작동합니다.
let jsonObject = response.json() as Object;
let fooInstance = plainToClass(Models.Foo, jsonObject);
return fooInstance;
중첩 된 자식을 지원하지만 클래스 멤버를 장식해야합니다.
@Type
그러나 주석을 잊지 마십시오 ). 이 답변에는 더 많은 크레딧이 필요합니다.
TypeScript에서는 다음 과 같은 인터페이스와 제네릭을 사용하여 형식 어설 션 을 수행 할 수 있습니다 .
var json = Utilities.JSONLoader.loadFromFile("../docs/location_map.json");
var locations: Array<ILocationMap> = JSON.parse(json).location;
여기서 ILocationMap은 데이터 모양을 설명합니다. 이 방법의 장점은 JSON에 더 많은 속성을 포함 할 수 있지만 모양이 인터페이스의 조건을 충족한다는 것입니다.
도움이 되길 바랍니다.
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()
})
그러나 슬프게도이 방법 은 중첩 객체 에서 작동하지 않습니다 .
Object.create(MyClass.prototype)
무시하고 대상 인스턴스를 통해 생성 할 수 있습니다 .
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
TLDR : 하나의 라이너
// This assumes your constructor method will assign properties from the arg.
.map((instanceData: MyClass) => new MyClass(instanceData));
자세한 답변
나는 것 없는 이 클래스 자체 내에서 부적절 쓰레기 수업 관련이없는 속성이 예 (뿐만 아니라 정의 폐쇄) 선언되지 않은 수대로 Object.assign 방법을 추천합니다.
역 직렬화하려는 클래스에서 역 직렬화하려는 속성이 정의되어 있는지 확인합니다 (null, empty array 등). 초기 값으로 속성을 정의하면 클래스 멤버를 반복하여 값을 할당하려고 할 때 가시성을 노출합니다 (아래의 직렬화 해제 방법 참조).
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];
}
}
}
}
위의 예에서 간단히 deserialize 메서드를 만들었습니다. 실제 예에서는 재사용 가능한 기본 클래스 또는 서비스 방법으로 중앙 집중화해야합니다.
여기에 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와 작업 목록을 각각의 클래스 인스턴스로 역 직렬화합니다.
json에 typescript 클래스와 동일한 속성이 있다고 가정하면 Json 속성을 typescript 객체에 복사 할 필요가 없습니다. 생성자에서 json 데이터를 전달하는 Typescript 객체를 구성해야합니다.
Ajax 콜백에서 회사를받습니다 :
onReceiveCompany( jsonCompany : any )
{
let newCompany = new Company( jsonCompany );
// call the methods on your newCompany object ...
}
그 일을하기 위해 :
1) json 데이터를 매개 변수로 사용하는 생성자를 Typescript 클래스에 추가하십시오. 해당 생성자에서 다음과 같이 jQuery로 json 객체를 확장합니다 $.extend( this, jsonData)
. $ .extend를 사용하면 json 객체의 속성을 추가하면서 자바 스크립트 프로토 타입을 유지할 수 있습니다.
2) 연결된 객체에 대해서도 동일한 작업을 수행해야합니다. 예제의 Employees의 경우 직원에 대한 json 데이터의 일부를 사용하는 생성자를 작성하십시오. json 직원을 typescript Employee 객체로 변환하려면 $ .map을 호출하십시오.
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 객체를 다룰 때 찾은 최고의 솔루션입니다.
서버에서 수신 한 JSON 오브젝트에 예상 한 (읽기 준수) 유형 스크립트의 인터페이스 특성이 있는지 자동으로 확인할 것은 없습니다. 그러나 사용자 정의 유형 가드를 사용할 수 있습니다
다음 인터페이스와 바보 같은 json 객체 (모든 유형 일 수 있음)를 고려하십시오.
interface MyInterface {
key: string;
}
const json: object = { "key": "value" }
세 가지 가능한 방법 :
A. 변수 뒤에 놓인 Type Assertion 또는 단순 정적 캐스트
const myObject: MyInterface = json as MyInterface;
B. 변수 앞과 다이아몬드 사이의 간단한 정적 캐스트
const myObject: MyInterface = <MyInterface>json;
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}'.`);
}
이 예제를 가지고 놀 수 있습니다
여기서 어려움은 isMyInterface
함수 를 작성하는 것입니다. TS가 장식자를 조만간 추가 하여 런타임에 복잡한 타이핑 을 내보내고 런타임이 필요할 때 객체의 구조를 확인하도록하겠습니다. 지금까지는 거의 동일한 목적 또는이 런타임 유형 검사 함수 생성기 인 JSON 스키마 유효성 검증기를 사용할 수 있습니다.
제 경우에는 효과가 있습니다. Object.assign (target, sources ...) 함수를 사용했습니다 . 먼저 올바른 객체를 만든 다음 json 객체에서 대상으로 데이터를 복사합니다.
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;
}
}
this.users[i] = new User(); Object.assign(this.users[i], users[i])
this.users[i] = Object.assign(new User(), users[i]);
그것이 캐스팅되지 않는 동안; 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"
대부분 정확하지만 매우 효율적인 답변이없는 오래된 질문입니다. 이것이 내가 제안하는 것 :
init () 메소드와 정적 캐스트 메소드 (단일 오브젝트 및 배열) 를 포함하는 기본 클래스를 작성하십시오 . 정적 메소드는 어디에나있을 수 있습니다. 기본 클래스와 init ()가 있는 버전은 나중에 쉽게 확장 할 수 있습니다.
export class ContentItem {
// parameters: doc - plain JS object, proto - class we want to cast to (subclass of ContentItem)
static castAs<T extends ContentItem>(doc: T, proto: typeof ContentItem): T {
// if we already have the correct class skip the cast
if (doc instanceof proto) { return doc; }
// create a new object (create), and copy over all properties (assign)
const d: T = Object.create(proto.prototype);
Object.assign(d, doc);
// reason to extend the base class - we want to be able to call init() after cast
d.init();
return d;
}
// another method casts an array
static castAllAs<T extends ContentItem>(docs: T[], proto: typeof ContentItem): T[] {
return docs.map(d => ContentItem.castAs(d, proto));
}
init() { }
}
@ Adam111p post에서 이와 유사한 역학 ( assign () 사용 )이 언급되었습니다. 또 다른 (보다 완전한) 방법입니다. @Timothy Perez는 assign ()의 중요 하게 여기지만 imho는 여기에 완전히 적합합니다.
파생 (실제) 클래스를 구현하십시오.
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);
}
}
}
이제 서비스에서 검색 한 객체를 캐스트 할 수 있습니다.
const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);
SubjectArea 객체 의 모든 계층 에는 올바른 클래스가 있습니다.
사용 사례 / 예; Angular 서비스를 작성하십시오 (기본 클래스를 다시 추상화하십시오).
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;
}
}
사용법이 매우 간단 해집니다. 영역 서비스를 작성하십시오.
@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
path = 'area';
subClass = SubjectArea;
constructor(protected http: Http) { super(); }
}
서비스의 get () 메소드는 이미 SubjectArea 객체 로 캐스트 된 배열의 약속을 반환 합니다 (전체 계층 구조)
이제 또 다른 수업이 있습니다.
export class OtherItem extends ContentItem {...}
데이터를 검색하고 올바른 클래스로 캐스트하는 서비스 작성은 다음과 같이 간단합니다.
@Injectable()
export class OtherItemService extends BaseService<OtherItem> {
path = 'other';
subClass = OtherItem;
constructor(protected http: Http) { super(); }
}
이 사이트를 사용하여 프록시를 생성 할 수 있습니다. 클래스를 생성하고 입력 JSON 객체를 구문 분석하고 확인할 수 있습니다.
나는 비슷한 요구에 부딪쳤다. REST API 호출에서 특정 클래스 정의로 또는 JSON에서 오는 JSON으로 쉽게 변환 할 수있는 것을 원했습니다. 내가 찾은 솔루션이 불충분하거나 클래스 코드를 다시 작성하고 주석 또는 유사한 것을 추가하기위한 것입니다.
Java에서 GSON과 같은 클래스를 사용하여 JSON 객체와 클래스를 직렬화 / 직렬화 해제하고 싶었습니다.
이후의 요구와 함께 변환기가 JS에서도 작동해야한다는 내 자신의 패키지 작성을 마쳤습니다.
그러나 약간의 오버 헤드가 있습니다. 그러나 시작되면 추가 및 편집이 매우 편리합니다.
다음을 사용하여 모듈을 초기화하십시오.
그런 다음 코드에서 초기화 된 모듈을 다음과 같이 사용합니다.
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
에서 Angular 사용을 위해 랩핑했습니다 .
객체를 그대로 클래스 생성자에 전달하십시오. 컨벤션이나 수표 없음
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
이 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',
// }
개인적으로 typescript가 엔드 포인트 정의가 수신중인 오브젝트의 유형을 지정하도록 허용하지 않는다는 사실을 알게되었습니다. 이것이 사실 인 것처럼, 나는 다른 언어로 한 일을 할 것입니다. 즉, 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가 제공하는 형식 안전성에 의존합니다.
'as'선언을 사용하십시오.
const data = JSON.parse(response.data) as MyClass;
MyClass
.
나는이 라이브러리를 여기에서 사용했다 : 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 값을 구문 분석해야합니다.
Java 객체를 반환하는 백엔드의 프론트 엔드 및 스프링 부트 응용 프로그램에서 Angular 6을 사용하고 있습니다. 내가해야 할 일은 일치하는 속성을 가진 각도 응용 프로그램에서 비슷한 클래스를 정의하는 것입니다. 그런 다음 각도 클래스 객체 ' 회사 '로 객체를 수락 할 수 있습니다 와 .
예를 들어 아래의 프론트 엔드 코드를 참조하십시오. 더 명확해야하는 것이 있으면 의견으로 알려주십시오.
createCompany() {
let company = new Company();
company.name = this.companyName;
this.companyService.createCompany(company).subscribe(comp => {
if (comp !== null) {
this.employerAdminCompany = comp as Company;
}
});
}
회사는 스프링 부트 앱의 엔티티 객체이며 Angular의 클래스이기도합니다.
export class Company {
public id: number;
public name: string;
public owner: User;
public locations: Array<Location>;
}