Programing

JSON 객체를 typescript 클래스로 캐스트하는 방법

lottogame 2020. 3. 4. 08:07
반응형

JSON 객체를 typescript 클래스로 캐스트하는 방법


원격 REST 서버에서 JSON 객체를 읽습니다. 이 JSON 객체에는 디자인에 따라 유형 스크립트 클래스의 모든 속성이 있습니다. 수신 된 JSON 객체를 유형 var로 캐스트하는 방법

타입 스크립트 var를 채우고 싶지 않습니다 (즉,이 JSON 객체를 취하는 생성자가 있습니다). 크기가 크며 하위 개체 및 속성별로 속성을 복사하는 데 많은 시간이 걸립니다.

업데이트 : 그러나 타이프 스크립트 인터페이스로 캐스트 할 수 있습니다 !


Ajax 요청의 일반 오래된 JavaScript 결과를 프로토 타입 JavaScript / TypeScript 클래스 인스턴스로 간단히 캐스트 할 수 없습니다. 이를 수행하는 데는 여러 가지 기술이 있으며 일반적으로 데이터 복사가 필요합니다. 클래스의 인스턴스를 만들지 않으면 메서드 나 속성이 없습니다. 간단한 JavaScript 객체로 유지됩니다.

데이터 만 다루는 경우 인터페이스에 캐스트를 수행 할 수 있지만 (순전히 컴파일 시간 구조이므로) 데이터 인스턴스를 사용하고 해당 데이터에 대한 작업을 수행하는 TypeScript 클래스를 사용해야합니다.

데이터 복사의 몇 가지 예 :

  1. 기존 객체에 AJAX JSON 객체 복사
  2. JavaScript에서 JSON 문자열을 특정 객체 프로토 타입으로 구문 분석

본질적으로, 당신은 단지 :

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

나는 같은 문제가 있었고 https://github.com/pleerock/class-transformer 작업을 수행하는 라이브러리를 찾았습니다 .

다음과 같이 작동합니다.

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

중첩 된 자식을 지원하지만 클래스 멤버를 장식해야합니다.


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()
})

그러나 슬프게도이 방법은 중첩 객체에서 작동하지 않습니다.


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) jscript 데이터를 매개 변수로 사용하는 생성자를 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 객체를 다룰 때 찾은 최고의 솔루션입니다.


제 경우에는 효과가 있습니다. 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;
  }
}

서버에서 수신 한 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 스키마 유효성 검증기를 사용할 수 있습니다.


그것이 캐스팅되지 않는 동안; 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"

interface유형 ( SomeType)을 만들어 그 안에 객체를 캐스트 할 수 있습니다 .

const typedObject: SomeType = <SomeType> responseObject;

대부분 정확하지만 매우 효율적이지 않은 오래된 질문. 이것이 내가 제안하는 것 :

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 () 사용 )이 언급되었습니다. 또 다른 (보다 완전한) 방법입니다. @ 티모시 페레즈는 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(); }
}

나는이 라이브러리를 여기에서 사용했다 : 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 값을 구문 분석해야합니다.


늦은 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
}

나는 비슷한 요구에 부딪쳤다. REST API 호출에서 특정 클래스 정의로 또는 JSON에서 오는 JSON으로 쉽게 변환 할 수있는 것을 원했습니다. 내가 찾은 솔루션이 불충분하거나 클래스 코드를 다시 작성하고 주석 또는 유사한 것을 추가하기위한 것입니다.

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
에서 Angular 사용을 위해 랩핑했습니다 .


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

이것은 간단하고 정말 좋은 옵션입니다

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

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

그리고 당신은 할 것이다

objectConverted.name

객체를 그대로 클래스 생성자에 전달하십시오. 컨벤션이나 수표 없음

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

참고 URL : https://stackoverflow.com/questions/22875636/how-do-i-cast-a-json-object-to-a-typescript-class



반응형