ฉันจะส่งวัตถุ JSON ไปยังคลาส typescript ได้อย่างไร


393

ฉันอ่านวัตถุ JSON จากเซิร์ฟเวอร์ REST ระยะไกล วัตถุ JSON นี้มีคุณสมบัติทั้งหมดของคลาส typescript (โดยการออกแบบ) ฉันจะส่งวัตถุที่ได้รับ JSON ไปยังประเภท var ได้อย่างไร

ฉันไม่ต้องการเติม var typescript (เช่นมีนวกรรมิกที่ใช้วัตถุ JSON นี้) มันมีขนาดใหญ่และการคัดลอกทุกสิ่งในวัตถุย่อยโดยวัตถุย่อยและทรัพย์สินตามคุณสมบัติจะใช้เวลานาน

อัปเดต:อย่างไรก็ตามคุณสามารถส่งไปยังอินเตอร์เฟสของ typescript ได้!


คุณสามารถใช้github.com/vojtechhabarta/typescript-generatorเพื่อสร้างอินเทอร์เฟซ TypeScript ในกรณีที่ JSON ของคุณถูกแมปโดยใช้คลาส Java
Vojta

ฉันเขียนรหัสห้องสมุดการคัดเลือกนักแสดงขนาดเล็ก: sulphur-blog.azurewebsites.net/typescript-mini-cast-library
Camille Wintz

1
วัตถุ JSON = วัตถุสัญลักษณ์วัตถุ JavaScript มีวัตถุไม่เพียงพอในนั้นฉันว่าเราโยนอีกสองสามอย่างเพื่อวัดที่ดี
bug-a-lot

1
ฉันได้ทำเครื่องมือสำหรับbeshanoe.github.io/json2ts
beshanoe

การสร้างคลาส TypeScript ต้นแบบเพื่อกำหนดวัตถุของคุณจะไม่ทำลายรหัสการผลิตจริง ลองดูที่ไฟล์ JS ที่คอมไพล์คำจำกัดความทั้งหมดจะถูกลบเนื่องจากมันไม่ได้เป็นส่วนหนึ่งของ JS
FisNaN

คำตอบ:


167

คุณไม่สามารถส่งผลลัพธ์ธรรมดาธรรมดา JavaScript จากคำขอ Ajax ไปยังอินสแตนซ์คลาส JavaScript / TypeScript ต้นแบบ มีหลายเทคนิคในการทำและโดยทั่วไปเกี่ยวข้องกับการคัดลอกข้อมูล ถ้าคุณไม่สร้างอินสแตนซ์ของคลาสมันจะไม่มีเมธอดหรือคุณสมบัติใด ๆ มันจะยังคงเป็นวัตถุ JavaScript ที่เรียบง่าย

ในขณะที่ถ้าคุณกำลังจัดการกับข้อมูลเพียงอย่างเดียวคุณก็สามารถส่งไปยังอินเทอร์เฟซ (เนื่องจากเป็นโครงสร้างเวลาการคอมไพล์อย่างหมดจด) ซึ่งจะต้องให้คุณใช้คลาส TypeScript ซึ่งใช้อินสแตนซ์ข้อมูลและดำเนินการกับข้อมูลนั้น

ตัวอย่างของการคัดลอกข้อมูล:

  1. การคัดลอกวัตถุ AJAX JSON ไปยังวัตถุที่มีอยู่
  2. แยกสตริง JSON ลงในต้นแบบวัตถุเฉพาะใน JavaScript

ในสาระสำคัญคุณเพียงแค่:

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

ฉันเห็นด้วยกับคำตอบของคุณ JSON.parse()ในฐานะที่เป็นนอกจากนี้ถึงแม้ว่าฉันไม่ได้อยู่ในสถานที่ที่จะมองมันได้และทดสอบมันตอนนี้ผมคิดว่าทั้งสองขั้นตอนสามารถนำมารวมกันโดยให้ฟังก์ชั่นปลุกเป็นพารามิเตอร์ไป ทั้งสองจะยังคงต้องทำ แต่ syntactically พวกเขาสามารถรวม
JAAulde

แน่นอนว่าอาจใช้งานได้เช่นกัน - ฉันไม่มีความรู้สึกว่าจะมีประสิทธิภาพมากขึ้นหรือไม่แม้ว่าจะต้องเรียกการเรียกฟังก์ชันพิเศษสำหรับแต่ละคุณสมบัติ
WiredPrairie

ไม่ใช่คำตอบที่ฉันกำลังมองหา :( ด้วยความอยากรู้อยากเห็นว่าทำไมนี่มันดูเหมือนว่าฉันจะใช้งานจาวาสคริปต์ว่าสิ่งนี้ควรจะทำได้
David Thielen

ไม่มันไม่ทำงานใน TypeScript เพราะไม่มีวิธีง่ายๆใน JavaScript ในการทำเช่นนี้
WiredPrairie

1
แล้วObject.setPrototypeOf
ล่ะ

102

ฉันมีปัญหาเดียวกันและฉันได้พบห้องสมุดที่จะทำงานนี้: https://github.com/pleerock/class-transformer

มันได้ผลเช่นนี้:

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

สนับสนุนเด็กซ้อน แต่คุณต้องตกแต่งสมาชิกในชั้นเรียนของคุณ


14
ห้องสมุดขนาดเล็กที่ยอดเยี่ยมแห่งนี้แก้ไขได้อย่างสมบูรณ์แบบโดยใช้ความพยายามน้อยที่สุด (อย่าลืม@Typeคำอธิบายประกอบของคุณ) คำตอบนี้สมควรได้รับเครดิตมากขึ้น
Benny Bottema

โอ้ว้าว! ห้องสมุดนี้มีขนาดเล็กมากบางทีอาจมีทุกสิ่งที่คุณต้องการแม้แต่ให้คุณควบคุมการเปลี่ยนแปลงด้วย @transform decorator: D
Diego Fernando Murillo Valenci

3
โปรดจำไว้ว่าห้องสมุดนี้ไม่ได้รับการดูแลอีกต่อไป มันไม่ทำงานกับ Angular5 + อีกต่อไปและเนื่องจากพวกเขาไม่ได้รวมคำขอดึงอีกต่อไปฉันไม่คิดว่าพวกเขาจะทำงานในเวลาใด ๆ ในไม่ช้า มันเป็นห้องสมุดที่ยอดเยี่ยม
kentor

มีวิธีแก้ปัญหาสำหรับ Angular5 + (จริงๆแล้วมันคือ Angular Bug): github.com/typestack/class-transformer/issues/108
Pak

2
มันใช้งานได้ดีใน Angular 6 (อย่างน้อยสำหรับกรณีการใช้งานของฉันซึ่งเป็นเพียงการแมป JSON <=> คลาส) อย่าง
แท้จริง

54

ใน TypeScript คุณสามารถทำการยืนยันประเภทโดยใช้อินเตอร์เฟสและชื่อสามัญดังนี้:

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

ตำแหน่งที่ ILocationMap อธิบายถึงรูปร่างของข้อมูลของคุณ ข้อดีของวิธีนี้คือ JSON ของคุณอาจมีคุณสมบัติมากกว่านี้ แต่รูปร่างตรงตามเงื่อนไขของอินเตอร์เฟส

ฉันหวังว่าจะช่วย!


49
FYI: มันเป็นการยืนยันประเภทไม่ใช่นักแสดง
WiredPrairie

6
ดูที่นี่สำหรับความแตกต่างระหว่างการยืนยันประเภทและหล่อ
Stefan Hanke

7
ฉันจะหายูทิลิตี้ JSONLoader ได้ที่ไหน
HypeXR

22
แต่มันจะไม่มีวิธีการใด ๆ ตามที่ระบุไว้ในคำตอบ
Martín Coll

1
@StefanHanke ดูเหมือนว่า URL จะเปลี่ยนไปเล็กน้อย: "Type Assertion vs. Casting"
ruffin

37

หากคุณกำลังใช้ 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()
})

แต่วิธีนี้จะไม่ทำงานกับวัตถุรังอย่างน่าเศร้า


4
พวกเขาถามหามันใน typescript
joe.feser

สวัสดี @ joe.feser ฉันพูดถึง ES6 เพราะวิธีนี้จำเป็นต้องใช้วิธีการ 'Object.assign'
migcoder

1
ในกรณีที่ไม่มีคอนสตรัคเตอร์เริ่มต้นอินสแตนซ์เป้าหมายสามารถสร้างขึ้นได้โดยObject.create(MyClass.prototype)ข้ามคอนสตรัคเตอร์ทั้งหมด
Marcello

คำอธิบายเพิ่มเติมเกี่ยวกับข้อ จำกัด ของวัตถุที่ซ้อนกันดูในstackoverflow.com/questions/22885995/…
Michael Freidgeim

28

ฉันพบบทความที่น่าสนใจมากเกี่ยวกับการคัดเลือก JSON ไปเป็น typescript Class:

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

20

TLDR: หนึ่งซับ

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

คำตอบโดยละเอียด

ฉันจะไม่แนะนำวิธีการ Object.assign เนื่องจากมันจะทำให้ตัวอย่างคลาสของคุณไม่เหมาะสมด้วยคุณสมบัติที่ไม่เกี่ยวข้อง (รวมถึงการปิดที่กำหนดไว้) ซึ่งไม่ได้ประกาศภายในคลาสเอง

ในชั้นเรียนที่คุณกำลังพยายามทำการ deserialize ให้ฉันแน่ใจว่ามีการกำหนดคุณสมบัติที่คุณต้องการ deserialized (โมฆะอาร์เรย์ที่ว่างเปล่า ฯลฯ ) ด้วยการกำหนดคุณสมบัติของคุณด้วยค่าเริ่มต้นคุณจะเปิดเผยการมองเห็นของพวกเขาเมื่อพยายามย้ำสมาชิกชั้นเรียนเพื่อกำหนดค่าให้กับ (ดูวิธี deserialize ด้านล่าง)

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

หากคุณมีสมาชิกคลาสที่เป็นประเภทเฉพาะ (aka: อินสแตนซ์ของคลาสอื่น) จากนั้นคุณสามารถให้สมาชิกเหล่านั้นแบ่งเป็นอินสแตนซ์ที่พิมพ์ด้วยวิธีการ 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));
  }
}

ตัวอย่างด้านบนจะยกเลิกการจัดทำทั้งบัญชีและรายการงานลงในอินสแตนซ์ของคลาสที่เกี่ยวข้อง


ฉันได้รับข้อความแสดงข้อผิดพลาดนี้: พิมพ์ '{ชื่อ: สตริง, อายุ: หมายเลข, หมายเลข: หมายเลข}' ไม่สามารถแปลงเป็นประเภท 'บุคคล' ได้ คุณสมบัติ 'id' เป็นแบบส่วนบุคคลในประเภท 'บุคคล' แต่ไม่ใช่ในประเภท '{ชื่อ: สตริง, อายุ: หมายเลข, หมายเลข: หมายเลข}'
utiq

ฉันควรใช้สิ่งนี้กับ enums อย่างไร ฉันต้องใช้วิธีการชนิดเฉพาะและเพิ่ม getter และ setter ให้หรือไม่
Tadija Bagarić

@TimothyParez คุณตั้งค่างานเมื่อใด
Kay

ฉันพยายามทำสิ่งที่คล้ายกัน แต่อาร์เรย์งานของฉันว่างเปล่าเมื่อฉันเป็นคน console.log
Kay

สำหรับการคอมไพล์ฉันต้องเพิ่ม Index Signature ให้กับคลาส: เอ็กซ์ปอร์ตคลาส Person {[key: string]: any (... )}
Asimov

18

สมมติว่า json มีคุณสมบัติเช่นเดียวกับคลาส typescript ของคุณคุณไม่จำเป็นต้องคัดลอกคุณสมบัติ Json ของคุณไปยังวัตถุ typescript ของคุณ คุณจะต้องสร้างวัตถุ typescript ของคุณผ่านข้อมูล json ในตัวสร้าง

ในการโทรกลับ ajax ของคุณคุณจะได้รับ บริษัท :

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

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

เพื่อที่จะทำให้งานนั้น:

1) เพิ่ม Constructor ในคลาส typescript ของคุณซึ่งรับข้อมูล json เป็นพารามิเตอร์ ในตัวสร้างนั้นคุณขยายวัตถุ json ของคุณด้วย jQuery เช่นนี้$.extend( this, jsonData)เช่นนี้ $ .extend ให้การรักษาต้นแบบ javascript ในขณะที่เพิ่มคุณสมบัติของวัตถุ json

2) หมายเหตุคุณจะต้องทำเช่นเดียวกันสำหรับวัตถุที่เชื่อมโยง ในกรณีของพนักงานในตัวอย่างคุณสร้างตัวสร้างโดยใช้ส่วนของข้อมูล json สำหรับพนักงาน คุณเรียก $ .map เพื่อแปลพนักงาน json เป็นอ็อบเจกต์ typescript 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


ฉันชอบโซลูชันนี้มากกว่าการติดตั้งและดูแลรักษาอินเทอร์เฟซเนื่องจากแอปพลิเคชัน Angular2 ของฉันมีรูปแบบแอปพลิเคชันจริงที่อาจแตกต่างจากโมเดลของเว็บเซอร์แอปพลิเคชันของฉันที่ใช้งาน มันสามารถมีข้อมูลส่วนตัวและคุณสมบัติที่คำนวณได้
Anthony Brenelière

7
การใช้ JQuery ในโครงการเชิงมุมเป็นแนวคิดที่น่ากลัว และถ้าแบบจำลองของคุณมีฟังก์ชั่นมากมายมันก็ไม่ใช่แบบจำลองอีกต่อไป
Davor

1
@Davor คุณหมายถึง POJO หรือโมเดลใช่ไหม POJO (โดยทั่วไปวัตถุธรรมดา) ไม่มีฟังก์ชั่นในขณะที่รุ่นเป็นคำที่กว้างขึ้นและมันรวมถึงพื้นที่เก็บข้อมูล รูปแบบพื้นที่เก็บข้อมูลตรงกันข้ามกับ POJO เป็นเรื่องเกี่ยวกับฟังก์ชั่น แต่มันก็ยังคงรูปแบบ
forsberg

@Davor: การใช้ JQuery ในโครงการ Angular ไม่ใช่ความคิดที่ดีตราบใดที่คุณไม่ใช้เพื่อจัดการ DOM ซึ่งเป็นความคิดที่แย่มาก ฉันใช้ไลบรารีใด ๆ ที่ฉันต้องการสำหรับโครงการเชิงมุมของฉันและสำหรับ jQuery ไม่ใช่ตัวเลือกเนื่องจากโครงการของฉันใช้ SignalR ที่ขึ้นอยู่กับมัน ในกรณีของคลาสปัจจุบันใช้โดย javascript ES6 ข้อมูลจะถูกเข้าถึงด้วยคุณสมบัติที่เป็นฟังก์ชันที่ห่อหุ้มวิธีการจัดเก็บข้อมูลในหน่วยความจำ สำหรับช่างก่อสร้างมีวิธีการที่เหมาะสมในการใช้โรงงาน
Anthony Brenelière

OP มีความชัดเจนเกี่ยวกับตัวแบบข้อมูลธรรมดาสำหรับ REST คุณกำลังทำให้มันซับซ้อนโดยไม่จำเป็น และใช่คุณสามารถใช้ Jquery สำหรับสิ่งเพิ่มเติม แต่คุณกำลังนำเข้าห้องสมุดขนาดใหญ่เพื่อใช้ 1% ของมัน นั่นคือกลิ่นรหัสถ้าฉันเคยเห็นมา
Davor

18

ยังไม่มีอะไรจะตรวจสอบโดยอัตโนมัติว่าวัตถุ JSON ที่คุณได้รับจากเซิร์ฟเวอร์นั้นมีคุณสมบัติอินเทอร์เฟซของการอ่านของ typescript หรือไม่ แต่คุณสามารถใช้Guards ประเภทที่กำหนดโดยผู้ใช้

พิจารณาอินเทอร์เฟซต่อไปนี้และวัตถุ json ที่โง่ (อาจเป็นประเภทใดก็ได้):

interface MyInterface {
    key: string;
 }

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

สามวิธีที่เป็นไปได้:

A. ประเภทการยืนยันหรือการส่งแบบสแตติกแบบง่ายวางอยู่หลังตัวแปร

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 schema ซึ่งมีจุดประสงค์ที่จะประมาณค่าเดียวกันหรือฟังก์ชันตัวตรวจสอบชนิดรันไทม์นี้


1
คำตอบของคุณควรจะอยู่ด้านบนฉันคิดว่า
afzalex

อย่างแน่นอน. นี่คือคำตอบที่ดีที่สุดซึ่งอาศัยโดยตรงกับเว็บไซต์ ts
Burak Karakuş

15

ในกรณีของฉันมันใช้งานได้ ผมใช้ฟังก์ชั่น Object.assign (เป้าหมายแหล่งที่มา ... ) ขั้นแรกให้สร้างวัตถุที่ถูกต้องแล้วคัดลอกข้อมูลจากวัตถุ 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;
  }
}

จะเกิดอะไรขึ้นเมื่อคุณมีทรัพย์สินส่วนตัว
prasanthv

เนื่องจากวัตถุ jsonUser ไม่ใช่คลาส User ไม่มีการดำเนินการ Object.assign (u, jsonUsers); คุณไม่สามารถใช้ฟังก์ชั่น getId () หลังจากกำหนดคุณจะได้รับวัตถุผู้ใช้ที่ถูกต้องซึ่งคุณสามารถใช้ฟังก์ชัน getId () ฟังก์ชัน getId () เป็นเพียงตัวอย่างสำหรับการดำเนินการที่สำเร็จ
Adam111p

คุณสามารถข้าม temp var - เพียงแค่ทำthis.users[i] = new User(); Object.assign(this.users[i], users[i])
cyptus

หรือใช้ประโยชน์จากค่าส่งคืนได้ดีขึ้น:this.users[i] = Object.assign(new User(), users[i]);
Cyptus

รุ่นยาวนี้มีไว้สำหรับอธิบายเท่านั้น คุณสามารถร่นรหัสมากที่สุดเท่าที่คุณต้องการ :)
Adam111p

6

ในขณะที่มันไม่ได้หล่อต่อ se; ฉันพบ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"

1
มันไม่ได้หล่อมันทำอะไรจริงๆ?
DanielM

1
วิธีนี้ต้องใช้คำอธิบายประกอบมากมาย ไม่มีวิธีที่ง่ายกว่านี้จริงเหรอ?
JPNotADragon

3

คุณสามารถสร้างinterfaceประเภทของคุณ ( SomeType) และโยนวัตถุในนั้น

const typedObject: SomeType = <SomeType> responseObject;

3

หากคุณต้องการส่งวัตถุ json ของคุณไปยังคลาส typescript และมีวิธีการอินสแตนซ์ที่มีอยู่ในวัตถุที่เป็นผลลัพธ์ที่คุณต้องใช้Object.setPrototypeOfเช่นเดียวกับที่ฉันทำในรหัสตัวอย่างร้อง:

Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)

2

คำถามเก่าที่มีคำตอบส่วนใหญ่ถูกต้อง แต่ไม่มีประสิทธิภาพมาก นี่คือสิ่งที่ฉันเสนอ:

สร้างคลาสพื้นฐานที่มีเมธอดinit ()และเมธอด cast แบบสแตติก (สำหรับอ็อบเจ็กต์เดี่ยวและอาร์เรย์) วิธีการคงที่อาจเป็นที่ใดก็ได้; รุ่นที่มีคลาสฐานและ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() { }
}

กลศาสตร์ที่คล้ายกัน (ที่มีassign () ) ถูกกล่าวถึงใน @ Adam111p โพสต์ อีกวิธีหนึ่ง (สมบูรณ์มากขึ้น) ที่จะทำ @ Timothy Perez สำคัญยิ่งสำหรับมอบหมาย ()แต่ 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 ทั้งหมดจะมีคลาสที่ถูกต้อง

กรณีใช้งาน / ตัวอย่าง; สร้างบริการเชิงมุม (คลาสฐานนามธรรมอีกครั้ง):

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

ได้รับ ()วิธีการของการบริการจะกลับมาสัญญาของอาร์เรย์แล้วเป็นวัตถุSubjectArea (ลำดับชั้นทั้งหมด)

ตอนนี้พูดว่าเรามีคลาสอื่น:

export class OtherItem extends ContentItem {...}

การสร้างบริการที่ดึงข้อมูลและปลดเปลื้องไปยังคลาสที่ถูกต้องนั้นง่ายเหมือน:

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

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

2

ใช้คลาสที่ขยายจากอินเตอร์เฟส

แล้ว:

Object.assign(
                new ToWhat(),
                what
              )

และดีที่สุด:

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

สิ่งที่กลายเป็นตัวควบคุมของ DataInterface


1

https://jvilk.com/MakeTypes/

คุณสามารถใช้ไซต์นี้เพื่อสร้างพร็อกซีให้คุณ มันสร้างคลาสและสามารถแยกวิเคราะห์และตรวจสอบวัตถุ JSON ของคุณ


0

ใน lates 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
}

0

ฉันวิ่งเข้าไปในความต้องการที่คล้ายกัน ฉันต้องการบางสิ่งที่จะทำให้ฉันเปลี่ยนจาก / เป็น JSON ได้ง่ายซึ่งมาจากการเรียก REST api ไปยัง / จากการกำหนดคลาสเฉพาะ วิธีแก้ปัญหาที่ฉันพบนั้นไม่เพียงพอหรือตั้งใจจะเขียนรหัสชั้นเรียนของฉันอีกครั้งและเพิ่มคำอธิบายประกอบหรือ Similars

ฉันต้องการบางสิ่งเช่น GSON ที่ใช้ใน Java เพื่อทำให้เป็นอนุกรม / deserialize คลาสไปยัง / จากวัตถุ 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


0

ส่งต่อวัตถุตามที่เป็นไปที่ตัวสร้างคลาส ไม่มีการประชุมหรือตรวจสอบ

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

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',
// }

0

โดยส่วนตัวแล้วฉันพบว่ามันน่ากลัวที่ typescript ไม่อนุญาตให้นิยามของ endpoint ระบุประเภทของวัตถุที่ได้รับ ตามที่ปรากฏว่าเป็นกรณีนี้ฉันจะทำสิ่งที่ฉันทำกับภาษาอื่นและนั่นคือฉันจะแยกวัตถุ JSON ออกจากคำจำกัดความของคลาสและให้คำจำกัดความของคลาสใช้วัตถุ JSON เป็นข้อมูลสมาชิกเท่านั้น .

ฉันดูถูกรหัส boilerplate ดังนั้นสำหรับฉันมันมักจะเป็นเรื่องของการได้รับผลลัพธ์ที่ต้องการโดยมีจำนวนน้อยที่สุดของรหัสในขณะที่รักษาประเภท

พิจารณานิยามโครงสร้างอ็อบเจ็กต์ 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


0

ใช้คำประกาศ 'as':

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

เทคนิคนี้ถูกกล่าวถึงในคำตอบนี้เมื่อสองปีก่อนและตามที่ได้กล่าวถึงในที่อื่นไม่ได้เพิ่มฟังก์ชั่นใด ๆ ที่อาจประกาศMyClassใช้
นอกรีตลิง

-1

นี่เป็นตัวเลือกที่ง่ายและดีจริงๆ

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

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

แล้วคุณจะมี

objectConverted.name

-1

ฉันใช้ห้องสมุดนี้ที่นี่: 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));
}

บางครั้งคุณจะต้องวิเคราะห์ค่า JSON สำหรับ plainToClass เพื่อทำความเข้าใจว่าเป็นข้อมูลที่จัดรูปแบบ JSON


แนะนำไลบรารี 'class-transformer' ในคำตอบอื่น ๆ ด้านบนstackoverflow.com/a/40042282/52277
Michael Freidgeim

-1

ฉันใช้ Angular 6 ในส่วนหน้าและ Spring Boot แอปพลิเคชันที่แบ็กเอนด์ซึ่งส่งคืน Java Objects สิ่งที่ฉันต้องทำคือกำหนดคลาสที่คล้ายกันในแอปพลิเคชันเชิงมุมซึ่งมีคุณสมบัติที่ตรงกันและจากนั้นฉันสามารถยอมรับวัตถุ 'เป็น' คลาสวัตถุเชิงมุม ( คอมพ์เป็น บริษัทในตัวอย่างด้านล่าง)

อ้างถึงรหัสส่วนหน้าด้านล่างเช่น แจ้งให้เราทราบในความคิดเห็นหากมีอะไรต้องการความชัดเจนมากขึ้น

  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>;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.