Wie wandle ich ein JSON-Objekt in eine Typoskriptklasse um?

393

Ich habe ein JSON-Objekt von einem Remote-REST-Server gelesen. Dieses JSON-Objekt verfügt über alle Eigenschaften einer Typoskriptklasse (beabsichtigt). Wie wandle ich das empfangene JSON-Objekt in einen Typ var um?

Ich möchte keine Typoskript-Variable füllen (dh einen Konstruktor haben, der dieses JSON-Objekt verwendet). Es ist groß und das Kopieren von Unterobjekten für Unterobjekte und Eigenschaften für Eigenschaften würde viel Zeit in Anspruch nehmen.

Update: Sie können es jedoch in eine Typoskript-Oberfläche umwandeln!

David Thielen
quelle
Sie können github.com/vojtechhabarta/typescript-generator verwenden , um TypeScript-Schnittstellen zu generieren, falls Ihr JSON mithilfe von Java-Klassen zugeordnet wird
Vojta
Ich habe eine kleine Casting-Bibliothek codiert: sulphur-blog.azurewebsites.net/typescript-mini-cast-library
Camille Wintz
1
JSON-Objekt = JavaScript-Objektnotationsobjekt. Hat nicht genug Gegenstände drin, ich sage, wir werfen ein paar mehr für ein gutes Maß ein.
Bug-a-Lot
1
Ich habe ein Werkzeug für diese beshanoe.github.io/json2ts
beshanoe
Das Erstellen einer TypeScript-Prototypklasse zum Definieren Ihres Objekts beeinträchtigt den tatsächlichen Produktionscode nicht. Schauen Sie sich die kompilierte JS-Datei an, alle Definitionen werden entfernt, da sie nicht Teil von JS sind.
FisNaN

Antworten:

167

Sie können ein einfaches JavaScript-Ergebnis aus einer Ajax-Anforderung nicht einfach in eine prototypische JavaScript / TypeScript-Klasseninstanz umwandeln. Es gibt eine Reihe von Techniken, die im Allgemeinen das Kopieren von Daten umfassen. Sofern Sie keine Instanz der Klasse erstellen, verfügt sie über keine Methoden oder Eigenschaften. Es bleibt ein einfaches JavaScript-Objekt.

Wenn Sie sich nur mit Daten befassen, können Sie einfach eine Umwandlung in eine Schnittstelle durchführen (da es sich lediglich um eine Kompilierungszeitstruktur handelt). Dazu müssen Sie jedoch eine TypeScript-Klasse verwenden, die die Dateninstanz verwendet und Operationen mit diesen Daten ausführt.

Einige Beispiele zum Kopieren der Daten:

  1. Kopieren des AJAX JSON-Objekts in ein vorhandenes Objekt
  2. Analysieren Sie JSON-Zeichenfolgen in einen bestimmten Objektprototyp in JavaScript

Im Wesentlichen würden Sie nur:

var d = new MyRichObject();
d.copyInto(jsonResult);
WiredPrairie
quelle
Ich stimme Ihrer Antwort zu. Obwohl ich momentan nicht in der Lage bin, es nachzuschlagen und zu testen, denke ich, dass diese beiden Schritte kombiniert werden könnten, indem eine Weckfunktion als Parameter zugewiesen wird JSON.parse(). Beides müsste noch getan werden, aber syntaktisch könnten sie kombiniert werden.
JAAulde
Sicher, das könnte auch funktionieren - ich habe keine Ahnung, ob es effizienter wäre, da für jede Eigenschaft ein zusätzlicher Funktionsaufruf erforderlich wäre.
WiredPrairie
Auf jeden Fall nicht die Antwort, nach der ich gesucht habe :( Aus Neugier, warum ist das so? Es scheint mir, wie Javascript funktioniert, dass dies machbar sein sollte.
David Thielen
Nein, es funktioniert nicht in TypeScript, da es in JavaScript keine einfache Möglichkeit gibt, dies zu tun.
WiredPrairie
1
Was ist mitObject.setPrototypeOf
Petah
102

Ich hatte das gleiche Problem und habe eine Bibliothek gefunden, die diese Aufgabe erfüllt: https://github.com/pleerock/class-transformer .

Es funktioniert so:

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

Es unterstützt verschachtelte Kinder, aber Sie müssen das Mitglied Ihrer Klasse dekorieren.

Pak
quelle
14
Diese brillante kleine Bibliothek hat es mit geringstem Aufwand perfekt gelöst (vergessen Sie jedoch nicht Ihre @TypeAnmerkungen). Diese Antwort verdient mehr Anerkennung.
Benny Bottema
Oh wow!, Diese Bibliothek ist nicht so klein, dass sie vielleicht alles enthält, was Sie brauchen. Sie können sogar die Transformation mit dem @ transformator-Dekorateur steuern: D
Diego Fernando Murillo Valenci
3
Beachten Sie, dass diese Bibliothek kaum noch gepflegt wird. Es funktioniert nicht mehr mit Angular5 + und da sie nicht einmal mehr Pull-Anfragen zusammenführen, denke ich nicht, dass sie bald daran arbeiten werden. Es ist eine großartige Bibliothek.
Kentor
Es gibt eine Problemumgehung für Angular5 + (es ist eigentlich ein Angular Bug): github.com/typestack/class-transformer/issues/108
Pak
2
Dies funktioniert gut in Angular 6 (zumindest für meinen Anwendungsfall, der nur darin besteht, JSON <=> Class buchstäblich abzubilden)
tftd
54

In TypeScript können Sie eine Typzusicherung über eine Schnittstelle und Generika wie folgt durchführen:

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

Wobei ILocationMap die Form Ihrer Daten beschreibt. Der Vorteil dieser Methode besteht darin, dass Ihr JSON möglicherweise mehr Eigenschaften enthält, die Form jedoch die Bedingungen der Schnittstelle erfüllt.

Ich hoffe das hilft!

user756310
quelle
49
Zu Ihrer Information: Es ist eine Typbehauptung, keine Besetzung.
WiredPrairie
6
Sehen Sie hier den Unterschied zwischen einer Art Behauptung und einer Besetzung .
Stefan Hanke
7
Wo finde ich Utilities.JSONLoader?
HypeXR
22
Aber es wird keine Methoden geben, wie in der Antwort erwähnt.
Martín Coll
1
@StefanHanke sieht aus, als hätte sich die URL leicht geändert: "Type Assertion vs. Casting"
Ruffin
37

Wenn Sie ES6 verwenden, versuchen Sie Folgendes:

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

Aber dieser Weg funktioniert leider nicht auf dem Nestobjekt.

Migcoder
quelle
4
Sie fragten in Typescript danach.
Joe.Feser
HI @ joe.feser, ich erwähne ES6, weil auf diese Weise die Methode 'Object.assign' erforderlich ist.
Migcoder
1
Falls der Standardkonstruktor fehlt, kann die Zielinstanz unter Object.create(MyClass.prototype)Umgehung des Konstruktors erstellt werden.
Marcello
Weitere Erläuterungen zur Einschränkung verschachtelter Objekte finden Sie unter stackoverflow.com/questions/22885995/…
Michael Freidgeim,
28

Ich fand einen sehr interessanten Artikel über das generische Casting von JSON in eine Typescript-Klasse:

http://cloudmark.github.io/Json-Mapping/

Am Ende erhalten Sie folgenden 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
Philip
quelle
20

TLDR: Ein Liner

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

Die detaillierte Antwort

Ich würde nicht den Object.assign-Ansatz empfehlen, da er Ihre Klasseninstanz unangemessen mit irrelevanten Eigenschaften (sowie definierten Abschlüssen) verunreinigen kann, die nicht in der Klasse selbst deklariert wurden.

In der Klasse, in die Sie deserialisieren möchten, würde ich sicherstellen, dass alle Eigenschaften definiert sind, die deserialisiert werden sollen (null, leeres Array usw.). Indem Sie Ihre Eigenschaften mit Anfangswerten definieren, legen Sie deren Sichtbarkeit offen, wenn Sie versuchen, Klassenmitglieder zu iterieren, denen Werte zugewiesen werden sollen (siehe Deserialisierungsmethode unten).

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

Im obigen Beispiel habe ich einfach eine Deserialisierungsmethode erstellt. In einem Beispiel aus der Praxis würde ich es in einer wiederverwendbaren Basisklasse oder Dienstmethode zentralisieren lassen.

Hier erfahren Sie, wie Sie dies in so etwas wie einem http bzw. ...

this.http.get(ENDPOINT_URL)
  .map(res => res.json())
  .map((resp: Person) => new Person(resp) ) );

Wenn tslint / ide sich darüber beschwert, dass der Argumenttyp nicht kompatibel ist, wandeln Sie das Argument einfach in eckigen Klammern in denselben Typ um <YourClassName>. Beispiel:

const person = new Person(<Person> { name: 'John', age: 35, id: 1 });

Wenn Sie Klassenmitglieder eines bestimmten Typs haben (auch bekannt als: Instanz einer anderen Klasse), können Sie diese über Getter / Setter-Methoden in typisierte Instanzen umwandeln lassen.

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

Das obige Beispiel deserialisiert sowohl acct als auch die Liste der Aufgaben in ihre jeweiligen Klasseninstanzen.

Timothy Perez
quelle
Ich erhalte die folgende Fehlermeldung: Typ '{Name: Zeichenfolge, Alter: Nummer, ID: Nummer}' kann nicht in Typ 'Person' konvertiert werden. Die Eigenschaft 'id' ist privat vom Typ 'Person', jedoch nicht vom Typ '{Name: Zeichenfolge, Alter: Nummer, ID: Nummer}'
utiq
Wie soll ich das mit Aufzählungen verwenden? Muss ich den spezifischen Typansatz verwenden und Getter und Setter dafür hinzufügen?
Tadija Bagarić
@ TimothyParez Wann legen Sie die Aufgaben fest?
Kay
Ich habe versucht, etwas Ähnliches zu tun, aber mein Aufgabenarray ist leer, wenn ich console.log person.
Kay
Damit dies kompiliert werden konnte, musste ich der Klasse eine
Asimov
18

Angenommen, der JSON hat dieselben Eigenschaften wie Ihre Typoskriptklasse, müssen Sie Ihre Json-Eigenschaften nicht in Ihr Typoskriptobjekt kopieren. Sie müssen nur Ihr Typescript-Objekt erstellen, indem Sie die JSON-Daten im Konstruktor übergeben.

In Ihrem Ajax-Rückruf erhalten Sie eine Firma:

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

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

Damit das funktioniert:

1) Fügen Sie Ihrer Typescript-Klasse einen Konstruktor hinzu, der die JSON-Daten als Parameter verwendet. In diesem Konstruktor erweitern Sie Ihr json-Objekt mit jQuery wie folgt : $.extend( this, jsonData). Mit $ .extend können die Javascript-Prototypen beibehalten werden, während die Eigenschaften des JSON-Objekts hinzugefügt werden.

2) Beachten Sie, dass Sie dasselbe für verknüpfte Objekte tun müssen. Im Fall von Mitarbeitern im Beispiel erstellen Sie auch einen Konstruktor, der den Teil der JSON-Daten für Mitarbeiter übernimmt. Sie rufen $ .map auf, um json-Mitarbeiter in Typoskript-Mitarbeiterobjekte zu übersetzen.

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

Dies ist die beste Lösung, die ich beim Umgang mit Typescript-Klassen und JSON-Objekten gefunden habe.

Anthony Brenelière
quelle
Ich bevorzuge diese Lösung gegenüber der Implementierung und Wartung von Schnittstellen, da meine Angular2-Anwendungen ein echtes Anwendungsmodell haben, das sich möglicherweise vom Modell der von meiner Anwendung verwendeten Webservices unterscheidet. Es kann private Daten und berechnete Eigenschaften haben.
Anthony Brenelière
7
Die Verwendung von JQuery in Angular-Projekten ist eine schreckliche Idee. Und wenn Ihre Modelle eine Reihe von Funktionen enthalten, sind sie keine Modelle mehr.
Davor
1
@Davor Du meinst POJO oder Model? POJO (im Grunde einfache Objekte) haben keine Funktionen, während Modell ein weiter gefasster Begriff ist und ein Repository enthält. Im Gegensatz zu POJO handelt es sich beim Repository-Muster um Funktionen, aber es ist immer noch ein Modell.
Forsberg
@Davor: Die Verwendung von JQuery in Angular-Projekten ist keine schlechte Idee, solange Sie sie nicht zum Manipulieren des DOM verwenden, was in der Tat eine schreckliche Idee ist. Ich verwende jede Bibliothek, die ich für meine Angular-Projekte benötige, und für jQuery ist dies keine Option, da mein Projekt SignalR verwendet, das davon abhängt. Bei Klassen, die jetzt von Javascript ES6 verwendet werden, wird auf Daten mit Eigenschaften zugegriffen, die Funktionen sind, die die Art und Weise kapseln, wie die Daten im Speicher gespeichert werden. Für Konstrukteure gibt es einen geeigneten Weg, Fabriken zu verwenden.
Anthony Brenelière
Im OP geht es eindeutig um einfache Datenmodelle für REST. Du machst es unnötig kompliziert, Leute. Und ja, Sie können Jquery für zusätzliche Dinge verwenden, aber Sie importieren eine riesige Bibliothek, um 1% davon zu verwenden. Das ist ein Code-Geruch, wenn ich jemals einen gesehen habe.
Davor
18

Es gibt noch nichts, was automatisch überprüft werden könnte, ob das vom Server empfangene JSON-Objekt die erwarteten (read entspricht den) Schnittstelleneigenschaften des Typoskripts aufweist. Sie können jedoch benutzerdefinierte Typschutzvorrichtungen verwenden

Betrachtet man die folgende Schnittstelle und ein dummes JSON-Objekt (es könnte jeder Typ sein):

interface MyInterface {
    key: string;
 }

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

Drei mögliche Wege:

A. Typ Assertion oder einfache statische Umwandlung nach der Variablen

const myObject: MyInterface = json as MyInterface;

B. Einfacher statischer Guss vor der Variablen und zwischen Diamanten

const myObject: MyInterface = <MyInterface>json;

C. Erweiterte dynamische Umwandlung, Sie überprüfen selbst die Struktur des Objekts

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}'.`);
}

Sie können hier mit diesem Beispiel spielen

Beachten Sie, dass die Schwierigkeit hier darin besteht, die isMyInterfaceFunktion zu schreiben . Ich hoffe, TS wird früher oder später einen Dekorator hinzufügen, um komplexe Eingaben in die Laufzeit zu exportieren und die Laufzeit die Objektstruktur bei Bedarf überprüfen zu lassen. Im Moment können Sie entweder einen JSON-Schema-Validator verwenden, dessen Zweck ungefähr der gleiche ist, ODER diesen Generator für die Überprüfung des Laufzeit-Typs

Flavien Volken
quelle
1
Ihre Antwort sollte oben sein, denke ich
afzalex
Bestimmt. Dies ist die beste Antwort, die sich direkt auf die ts-Website stützt.
Burak Karakuş
15

In meinem Fall funktioniert es. Ich habe Funktionen Object.assign (Ziel, Quellen ...) verwendet . Zuerst wird das richtige Objekt erstellt und dann werden die Daten vom JSON-Objekt auf das Ziel kopiert. Beispiel:

let u:User = new User();
Object.assign(u , jsonUsers);

Und ein fortgeschritteneres Anwendungsbeispiel. Ein Beispiel mit dem 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;
  }
}
Adam111p
quelle
Was passiert, wenn Sie ein Privateigentum haben?
Prasanthv
Weil das jsonUser-Objekt keine Benutzerklasse ist. Ohne Operation Object.assign (u, jsonUsers); Sie können die Funktion getId () nicht verwenden. Erst nach dem Zuweisen erhalten Sie ein gültiges Benutzerobjekt, in dem Sie die Funktion getId () verwenden können. Die Funktion getId () gilt nur für das Beispiel, in dem die Operation erfolgreich war.
Adam111p
Sie können die Temp Var überspringen - einfach tunthis.users[i] = new User(); Object.assign(this.users[i], users[i])
Cyptus
oder noch besser nutzen Sie den Rückgabewert:this.users[i] = Object.assign(new User(), users[i]);
Cyptus
Diese lange Version dient nur zur Erklärung. Sie können den Code beliebig kürzen :)
Adam111p
6

Während es nicht per se gießt; Ich habe festgestellt, dass https://github.com/JohnWhiteTB/TypedJSON eine nützliche Alternative ist.

@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"
Neil
quelle
1
Es wird nicht gecastet, was macht es wirklich?
DanielM
1
Diese Lösung erfordert sehr viele Anmerkungen. Gibt es wirklich keinen einfacheren Weg?
JPNotADragon
3

Sie können ein Objekt interfaceIhres Typs ( SomeType) erstellen und das Objekt darin umwandeln.

const typedObject: SomeType = <SomeType> responseObject;
Jayant Varshney
quelle
3

Wenn Sie Ihr json-Objekt in eine Typoskriptklasse umwandeln müssen und die Instanzmethoden im resultierenden Objekt verfügbar sind, müssen Sie es verwenden Object.setPrototypeOf, wie ich es im folgenden Code-Snippet getan habe:

Object.setPrototypeOf(jsonObject, YourTypescriptClass.prototype)
s_bighead
quelle
2

Eine alte Frage mit meist richtigen, aber nicht sehr effizienten Antworten. Das was ich vorschlage:

Erstellen Sie eine Basisklasse, die die Methode init () und statische Umwandlungsmethoden enthält (für ein einzelnes Objekt und ein Array). Die statischen Methoden können überall sein; Die Version mit der Basisklasse und init () ermöglicht anschließend einfache Erweiterungen.

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

Ähnliche Mechaniken (mit assign () ) wurden in @ Adam111p post erwähnt. Nur eine andere (vollständigere) Möglichkeit, dies zu tun. @ Timothy Perez kritisiert zuweisen () , aber imho ist es hier völlig angemessen.

Implementieren Sie eine abgeleitete (die reale) Klasse:

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

Jetzt können wir ein aus dem Dienst abgerufenes Objekt umwandeln:

const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);

Alle Hierarchien von SubjectArea Objekten haben die richtige Klasse.

Ein Anwendungsfall / Beispiel; Erstellen Sie einen Angular-Service (wieder abstrakte Basisklasse):

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

Die Verwendung wird sehr einfach; Erstellen Sie einen Area-Service:

@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
  path = 'area';
  subClass = SubjectArea;

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

Die Methode get () des Dienstes gibt ein Versprechen eines Arrays zurück, das bereits als SubjectArea umgewandelt wurde Objekte umgewandelt wurde (gesamte Hierarchie).

Sagen wir jetzt, wir haben eine andere Klasse:

export class OtherItem extends ContentItem {...}

Das Erstellen eines Dienstes, der Daten abruft und in die richtige Klasse umwandelt, ist so einfach wie:

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

  constructor(protected http: Http) { super(); }
}
Normunds Kalnberzins
quelle
2

Verwenden Sie eine von einer Schnittstelle erweiterte Klasse.

Dann:

Object.assign(
                new ToWhat(),
                what
              )

Und das Beste:

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

ToWhat wird ein Controller von DataInterface.

Sam
quelle
1

https://jvilk.com/MakeTypes/

Sie können diese Site verwenden, um einen Proxy für Sie zu generieren. Es generiert eine Klasse und kann Ihr Eingabe-JSON-Objekt analysieren und validieren.

Amir Mehrabi
quelle
0

In den letzten TS können Sie Folgendes tun:

const isMyInterface = (val: any): val is MyInterface => {
  if (!val) { return false; }
  if (!val.myProp) { return false; }
  return true;
};

Und als Benutzer wie folgt:

if (isMyInterface(data)) {
 // now data will be type of MyInterface
}
Jaroslav
quelle
0

Ich hatte ein ähnliches Bedürfnis. Ich wollte etwas, das mir eine einfache Transformation von / zu JSON ermöglicht, die von einem REST-API-Aufruf zu / von einer bestimmten Klassendefinition stammt. Die Lösungen, die ich gefunden habe, waren unzureichend oder dazu gedacht, den Code meiner Klassen neu zu schreiben und Anmerkungen oder Ähnliches hinzuzufügen.

Ich wollte so etwas wie GSON wird in Java verwendet, um Klassen zu / von JSON-Objekten zu serialisieren / deserialisieren.

In Kombination mit der späteren Notwendigkeit, dass der Konverter auch in JS funktioniert, beendete ich das Schreiben meines eigenen Pakets.

Es hat jedoch ein wenig Overhead. Aber wenn es gestartet wird, ist es sehr praktisch beim Hinzufügen und Bearbeiten.

Sie initialisieren das Modul mit:

  1. Konvertierungsschema - Ermöglicht das Zuordnen zwischen Feldern und das Bestimmen, wie die Konvertierung durchgeführt wird
  2. Klassen ordnen Array zu
  3. Karte der Konvertierungsfunktionen - für spezielle Konvertierungen.

Dann verwenden Sie in Ihrem Code das initialisierte Modul wie folgt:

const convertedNewClassesArray : MyClass[] = this.converter.convert<MyClass>(jsonObjArray, 'MyClass');

const convertedNewClass : MyClass = this.converter.convertOneObject<MyClass>(jsonObj, 'MyClass');

oder an JSON:

const jsonObject = this.converter.convertToJson(myClassInstance);

Verwenden Sie diesen Link zum npm-Paket und eine ausführliche Erklärung zur Arbeit mit dem Modul: json-class-converter

Wickelte es auch für die
Verwendung in Angular ein in: Angular -JSON-Class-Converter

Doronm
quelle
0

Übergeben Sie das Objekt unverändert an den Klassenkonstruktor. Keine Konventionen oder Prüfungen

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
Lars Klingsten
quelle
0

Sie können dieses npm-Paket verwenden. https://www.npmjs.com/package/class-converter

Es ist einfach zu bedienen, zum Beispiel:

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',
// }
QC-Gepard
quelle
0

Persönlich finde ich es entsetzlich, dass Typoskript keine Endpunktdefinition zulässt, um den Typ des empfangenen Objekts anzugeben. Da dies tatsächlich der Fall zu sein scheint, würde ich das tun, was ich mit anderen Sprachen getan habe, und das heißt, ich würde das JSON-Objekt von der Klassendefinition trennen und die Klassendefinition das JSON-Objekt als einziges Datenelement verwenden lassen .

Ich verachte Boilerplate-Code, daher ist es für mich normalerweise eine Frage des Erreichens des gewünschten Ergebnisses mit der geringsten Menge an Code unter Beibehaltung des Typs.

Betrachten Sie die folgenden JSON-Objektstrukturdefinitionen - diese würden Sie an einem Endpunkt erhalten, es handelt sich nur um Strukturdefinitionen, keine Methoden.

interface IAddress {
    street: string;
    city: string;
    state: string;
    zip: string;
}

interface IPerson {
    name: string;
    address: IAddress;
}

Wenn wir das Obige objektorientiert betrachten, sind die obigen Schnittstellen keine Klassen, da sie nur eine Datenstruktur definieren. Eine Klasse in OO-Begriffen definiert Daten und den Code, der sie verarbeitet.

Also definieren wir jetzt eine Klasse, die Daten und den Code angibt, der sie verarbeitet ...

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

Und jetzt können wir einfach jedes Objekt übergeben, das der IPerson-Struktur entspricht, und uns auf den Weg machen ...

   Person person = new Person({
            name: "persons name",
            address: {
                street: "A street address",
                city: "a city",
                state: "a state",
                zip: "A zipcode"
            }
        });

Auf die gleiche Weise können wir jetzt das an Ihrem Endpunkt empfangene Objekt mit etwas in der Art von ...

Person person = new Person(req.body);    // As in an object received via a POST call

person.distanceFrom({ street: "Some street address", etc.});

Dies ist viel leistungsfähiger und beansprucht die Hälfte des Speichers zum Kopieren der Daten, während die Menge an Boilerplate-Code, die Sie für jeden Entitätstyp schreiben müssen, erheblich reduziert wird. Es basiert einfach auf der von TypeScript bereitgestellten Typensicherheit.

Rodney P. Barbati
quelle
0

Verwenden Sie die Erklärung 'as':

const data = JSON.parse(response.data) as MyClass;
Daniel Valdebenito
quelle
Diese Technik wurde in dieser Antwort vor zwei Jahren erwähnt und fügt, wie an anderer Stelle erörtert, keine Funktionen hinzu, für die deklariert werden kann MyClass.
Heretic Monkey
-1

Dies ist eine einfache und wirklich gute Option

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

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

Und dann wirst du haben

objectConverted.name
SamazoOo
quelle
-1

Ich habe diese Bibliothek hier verwendet: https://github.com/pleerock/class-transformer

<script lang="ts">
    import { plainToClass } from 'class-transformer';
</script>

Implementierung:

private async getClassTypeValue() {
  const value = await plainToClass(ProductNewsItem, JSON.parse(response.data));
}

Manchmal müssen Sie die JSON-Werte für plainToClass analysieren, um zu verstehen, dass es sich um JSON-formatierte Daten handelt

Mac Chibueze
quelle
Die 'Klassentransformator'-Bibliothek wird bereits in einer anderen Antwort über stackoverflow.com/a/40042282/52277
Michael Freidgeim
-1

Ich verwende Angular 6 im Frontend und Spring Boot-Anwendung im Backend, die Java-Objekte zurückgibt. Alles, was ich tun muss, ist, eine ähnliche Klasse in der Winkelanwendung zu definieren, die übereinstimmende Eigenschaften hat, und dann kann ich das Objekt als Winkelklassenobjekt akzeptieren ( comp als Firma) im folgenden Beispiel ).

Lesen Sie zum Beispiel den folgenden Front-End-Code. Lassen Sie mich in Kommentaren wissen, ob etwas mehr Klarheit benötigt.

  createCompany() {
    let company = new Company();
    company.name = this.companyName;

    this.companyService.createCompany(company).subscribe(comp => {
       if (comp !== null) {
        this.employerAdminCompany = comp as Company;
      }
    });
  }

Dabei ist Unternehmen ein Entitätsobjekt in der Spring Boot App und auch eine Klasse in Angular.

export class Company {
    public id: number;
    public name: string;
    public owner: User;
    public locations: Array<Location>;
}
Maneesh Parihar
quelle