web-dev-qa-db-fra.com

JSON à l'instance de classe TypeScript?

J'ai fait pas mal de recherches, mais je ne suis pas totalement satisfait de ce que j'ai trouvé. Juste pour être sûr, voici ma question: Quelle est en réalité la solution automatisée la plus robuste et la plus élégante pour la désérialisation de JSON en instances de classe d'exécution TypeScript?

Dis que j'ai eu ce cours:

class Foo {
  name: string;
  GetName(): string { return this.name };
}

Et disons que j'ai cette chaîne JSON pour la désérialisation:

{"name": "John Doe"}

Quelle est la solution la plus facile et la plus facile à gérer pour obtenir une instance d'une classe Foo avec le nom défini sur "John Doe" et la méthode GetName () pour fonctionner? Je demande très précisément parce que je sais qu'il est facile de désérialiser en un objet de données pur. Je me demande s'il est possible d'obtenir une instance de classe avec des méthodes de travail sans avoir à effectuer d'analyse manuelle ou de copie manuelle des données. Si une solution entièrement automatisée n'est pas possible, quelle est la meilleure solution?

88
Klaus

La question est assez large, je vais donc donner quelques solutions.

Solution 1: méthode d'assistance

Voici un exemple d'utilisation d'une méthode d'assistance que vous pouvez modifier pour répondre à vos besoins:

class SerializationHelper {
    static toInstance<T>(obj: T, json: string) : T {
        var jsonObj = JSON.parse(json);

        if (typeof obj["fromJSON"] === "function") {
            obj["fromJSON"](jsonObj);
        }
        else {
            for (var propName in jsonObj) {
                obj[propName] = jsonObj[propName]
            }
        }

        return obj;
    }
}

Puis en l'utilisant:

var json = '{"name": "John Doe"}',
    foo = SerializationHelper.toInstance(new Foo(), json);

foo.GetName() === "John Doe";

désérialisation avancée

Cela pourrait également permettre une certaine désérialisation personnalisée en ajoutant votre propre méthode fromJSON à la classe (cela fonctionne bien avec la façon dont JSON.stringify utilise déjà la méthode toJSON, comme cela sera montré):

interface IFooSerialized {
    nameSomethingElse: string;
}

class Foo {
  name: string;
  GetName(): string { return this.name }

  toJSON(): IFooSerialized {
      return {
          nameSomethingElse: this.name
      };
  }

  fromJSON(obj: IFooSerialized) {
        this.name = obj.nameSomethingElse;
  }
}

Puis en l'utilisant:

var foo1 = new Foo();
foo1.name = "John Doe";

var json = JSON.stringify(foo1);

json === '{"nameSomethingElse":"John Doe"}';

var foo2 = SerializationHelper.toInstance(new Foo(), json);

foo2.GetName() === "John Doe";

Solution 2: Classe de base

Pour ce faire, vous pouvez également créer votre propre classe de base:

class Serializable {
    fillFromJSON(json: string) {
        var jsonObj = JSON.parse(json);
        for (var propName in jsonObj) {
            this[propName] = jsonObj[propName]
        }
    }
}

class Foo extends Serializable {
    name: string;
    GetName(): string { return this.name }
}

Puis en l'utilisant:

var foo = new Foo();
foo.fillFromJSON(json);

Il y a trop de façons différentes de mettre en œuvre une désérialisation personnalisée à l'aide d'une classe de base, je vous laisse le soin de décider à votre guise.

68
David Sherret

Vous pouvez maintenant utiliser Object.assign(target, ...sources). Suivant votre exemple, vous pourriez l’utiliser comme ceci:

class Foo {
  name: string;
  getName(): string { return this.name };
}

let fooJson: string = '{"name": "John Doe"}';
let foo: Foo = Object.assign(new Foo(), JSON.parse(fooJson));

console.log(foo.getName()); //returns John Doe

Object.assign fait partie de ECMAScript 2015 et est actuellement disponible sur la plupart des navigateurs modernes.

44
Hugo Leao

Quelle est actuellement la solution automatisée la plus robuste et la plus élégante pour la désérialisation de JSON en instances de classe d'exécution TypeScript?

Utiliser décorateurs de propriétés avec ReflectDecorators pour enregistrer les informations de type accessibles à l'exécution pouvant être utilisées au cours d'un processus de désérialisation fournit un effet surprenant approche propre et largement adaptable, qui s’intègre parfaitement dans le code existant. Il est également entièrement automatisable et fonctionne également pour les objets imbriqués.

Une implémentation de cette idée est TypedJSON , que j’ai créée précisément pour cette tâche:

@JsonObject
class Foo {
    @JsonMember
    name: string;

    getName(): string { return this.name };
}
var foo = TypedJSON.parse('{"name": "John Doe"}', Foo);

foo instanceof Foo; // true
foo.getName(); // "John Doe"
13
John Weisz

Pourquoi ne pourrais-tu pas faire quelque chose comme ça?

class Foo {
  constructor(myObj){
     Object.assign(this, myObj);
  }
  get name() { return this._name; }
  set name(v) { this._name = v; }
}

let foo = new Foo({ name: "bat" });
foo.toJSON() //=> your json ...
4
amcdnl