web-dev-qa-db-fra.com

Ajouter un sérialiseur JSON à chaque classe de modèle?

En ce qui concerne l’encodage JSON dans Dart, cliquez sur la déclaration de Seth Ladd la méthode finalement officiellement approuvée est Dart:convert + JSON.Encode

Disons que nous avons un tas de classes de modèles ( PODO s) telles que:

class Customer
{
  int Id;
  String Name;
}

Maintenant, j'aimerais pouvoir simplement encoder JSON mes objets de domaine comme ceci:

var customer = new Customer()
  ..Id = 17
  ..Name = "John";
var json = JSON.encode(customer);

Malheureusement, ça ne marchera pas ...

Uncaught Error: Converting object to an encodable object failed.
Stack Trace: 
#0      _JsonStringifier.stringifyValue (Dart:convert/json.Dart:416)
#1      _JsonStringifier.stringify (Dart:convert/json.Dart:336)
#2      JsonEncoder.convert (Dart:convert/json.Dart:177)
....

... sauf si nous disons explicitement à Dart:convert comment encoder:

class Customer
{
  int Id;
  String Name;

  Map toJson() { 
    Map map = new Map();
    map["Id"] = Id;
    map["Name"] = Name;
    return map;
  }  
}

Dois-je vraiment ajouter une méthode toJson à chacune de mes classes de modèles ou existe-t-il une meilleure méthode?

EDIT: voici la sérialisation simple que je recherche:

{
    "Id": 17,
    "Name": "John"
}

Comparez à ToJson dans ServiceStack.Text , par exemple.

Dart's serialization library (voir la réponse de Matt B ci-dessous) semble être un pas dans la bonne direction. Cependant, cela ...

var serialization = new Serialization()
  ..addRuleFor(Customer); 
var json = JSON.encode(serialization.write(customer, format: new SimpleJsonFormat()));

... produit juste un tableau avec les valeurs (pas de clés):

[17,"John"]

L'utilisation de la valeur par défaut SimpleMapFormat génère par contre cette complex representation.

Je n'ai toujours pas trouvé ce que je cherchais ...

EDIT 2: Ajout d'un contexte: je crée un service Web RESTful dans Dart et je recherche une sérialisation JSON pouvant être facilement utilisée par tout client, pas seulement par un autre client Dart. Par exemple, interroger l'API Stack Exchange pour cette question même créera cette réponse JSON . C'est le format de sérialisation que je recherche. - Ou regardez les réponses JSON typiques renvoyées par l'API Twitter REST ou par l'API Graph Facebook .

EDIT 3: J'ai écrit un petit blog à ce sujet. Voir aussi la discussion sur Hacker News.

36
Max

IMO, il s’agit là d’un défaut majeur de Dart, surprenant compte tenu de son intérêt pour les applications Web. J'aurais pensé qu'avoir le support JSON dans les bibliothèques standard aurait signifié que la sérialisation des classes de et vers JSON fonctionnerait comme de l'eau. Malheureusement, le support JSON semble incomplet, où il semble que les choix sont de travailler avec des cartes faiblement typées ou de souffrir à l'aide d'un passe-partout inutile pour configurer vos classes standard (PODO) afin qu'elles soient sérialisées comme prévu.

Sans support de réflexion et de miroirs

Comme les plates-formes Dart populaires telles que Flutter ne supporte pas Reflection/Mirrors, votre seule option est d’utiliser une solution code-gen. L’approche que nous avons adoptée dans le prise en charge native de Dart et Flutter de ServiceStack vous permet de générer des modèles Dart typés pour tous vos services ServiceStack à partir d’une URL distante, par exemple:

$ npm install -g @servicestack/cli

$ Dart-ref https://www.techstacks.io

Pris en charge dans .NET Core et dans l’un des options d’hébergement populaires de .NET

L'exemple ci-dessus génère une API typée pour le projet { projet .NET Core 2.0 TechStacks } utilisant les DTO générés à partir de www.techstacks.io/types/Dart noeud final. Cela génère des modèles suivant le motif JsonCodec de Dart, dans lequel vous pouvez personnaliser la sérialisation de vos modèles de Dart en fournissant un constructeur nommé fromJson et une méthode d'instance toJson(). Voici un exemple de l'un des DTO générés:

class UserInfo implements IConvertible
{
    String userName;
    String avatarUrl;
    int stacksCount;

    UserInfo({this.userName,this.avatarUrl,this.stacksCount});
    UserInfo.fromJson(Map<String, dynamic> json) { fromMap(json); }

    fromMap(Map<String, dynamic> json) {
        userName = json['userName'];
        avatarUrl = json['avatarUrl'];
        stacksCount = json['stacksCount'];
        return this;
    }

    Map<String, dynamic> toJson() => {
        'userName': userName,
        'avatarUrl': avatarUrl,
        'stacksCount': stacksCount
    };

    TypeContext context = _ctx;
}

Avec ce modèle, vous pouvez utiliser la commande - dans json: convertir les API construite de Dart pour sérialiser et désérialiser votre modèle en JSON, par exemple:

//Serialization
var dto = new UserInfo(userName:"foo",avatarUrl:profileUrl,stacksCount:10);
String jsonString = json.encode(dto);

//Deserialization
Map<String,dynamic> jsonObj = json.decode(jsonString);
var fromJson = new UserInfo.fromJson(jsonObj);

L’avantage de cette approche est qu’elle fonctionne sur toutes les plates-formes Dart, y compris Flutter et AngularDart ou Dart Web Apps avec et sans le mode renforcé de Dart 2.

Les DTO générés peuvent également être utilisés avec le package Dart de servicestack pour permettre à une solution typée de bout en bout prenant en charge la sérialisation JSON dans et hors de vos DTO typés, par exemple:

var client = new JsonServiceClient("https://www.techstacks.io");
var response = await client.get(new GetUserInfo(userName:"mythz"));

Pour plus d'informations, consultez la documentation de support Dart natif de ServiceStack .

Dart avec des miroirs

Si vous utilisez Dart sur une plate-forme où la prise en charge de Mirrors est disponible, l'utilisation d'un Mixin nécessite peu d'efforts, par exemple:

import 'Dart:convert';
import 'Dart:mirrors';

abstract class Serializable {

  Map toJson() { 
    Map map = new Map();
    InstanceMirror im = reflect(this);
    ClassMirror cm = im.type;
    var decls = cm.declarations.values.where((dm) => dm is VariableMirror);
    decls.forEach((dm) {
      var key = MirrorSystem.getName(dm.simpleName);
      var val = im.getField(dm.simpleName).reflectee;
      map[key] = val;
    });

    return map;
  }  

}

Que vous pouvez mixer avec vos cours de PODO avec:

class Customer extends Object with Serializable
{
  int Id;
  String Name;
}

Que vous pouvez maintenant utiliser avec JSON.encode:

var c = new Customer()..Id = 1..Name = "Foo";

print(JSON.encode(c));

Résultat:

{"Id":1,"Name":"Foo"}

Remarque: voir mises en garde relatives à l'utilisation de miroirs

23
mythz

J'ai écrit la bibliothèque Exportable pour résoudre des problèmes tels que la conversion au format Map ou JSON. En l'utilisant, la déclaration de modèle se présente comme suit:

import 'package:exportable/exportable.Dart';

class Customer extends Object with Exportable {
  @export int id;
  @export String name;
}

Et si vous souhaitez convertir au format JSON, vous pouvez:

String jsonString = customer.toJson();

En outre, il est facile d'initialiser un nouvel objet à partir d'une chaîne JSON:

Customer customer = new Customer()..initFromJson(jsonString);

Ou bien:

Customer customer = new Exportable(Customer, jsonString);

Veuillez consulter le README pour plus d’informations.

14
Leksat

Une alternative consiste à utiliser le package Serialization et à ajouter des règles pour vos classes. La forme la plus élémentaire utilise la réflexion pour obtenir les propriétés automatiquement.

6
Matt B

Redstone mapper est la meilleure bibliothèque de sérialisation que j'ai utilisée. JsonObject et Exportable ont l'inconvénient d'étendre certaines de leurs classes. Avec Redstone Mapper, vous pouvez avoir des structures comme celle-ci

class News
{
    @Field() String title;
    @Field() String text;
    @Field() List<FileDb> images;
    @Field() String link; 
}

Cela fonctionne avec les getters et les setters, vous pouvez cacher des informations en ne les annotant pas avec @Field(), vous pouvez renommer le champ de/en json, avoir des objets imbriqués, cela fonctionne sur le serveur et le client. Il s’intègre également à la structure Redstone Server, où il dispose d’aides pour coder/décoder en MongoDB.

Le seul autre framework que j'ai vu qui soit dans la bonne direction est Dartson , mais il lui manque encore quelques fonctionnalités par rapport à Redstone Mapper.

6
Cristian Garcia

J'ai résolu avec:

class Customer extends JsonObject
{
  int Id;
  String Name;
  Address Addr;
}

class Address extends JsonObject{
  String city;
  String State;
  String Street;
}

Mais mon objectif est de lier les données de/à json à partir de/aux classes de modèle; Cette solution fonctionne si vous pouvez modifier les classes de modèle. Par contre, vous devez utiliser la solution "external" pour convertir les classes de modèle.

voir aussi: Analyse de la liste JSON avec la bibliothèque JsonObject dans Dart

3
Domenico Monaco

Un autre paquet résolvant ce problème est built_value:

https://github.com/google/built_value.Dart

Avec built_value, vos classes de modèle se présentent comme suit:

abstract class Account implements Built<Account, AccountBuilder> {
  static Serializer<Account> get serializer => _$accountSerializer;

  int get id;
  String get name;
  BuiltMap<String, JsonObject> get keyValues;

  factory Account([updates(AccountBuilder b)]) = _$Account;
  Account._();
}

Notez que built_value ne concerne pas seulement la sérialisation - il fournit également l'opérateur ==, le hashCode, toString et une classe de générateur.

1
David Morgan

J'ai utilisé dartson et cela me semble très facile et familier (si vous venez de Java)

0
Moshe Shaham