web-dev-qa-db-ja.com

JSONシリアライザーをすべてのモデルクラスに追加しますか?

DartでのJSONエンコードに関しては、 Seth Laddの発表 に従って、最終的に承認された公式の方法はDart:convert + JSON.Encodeです。

次のような多数のモデルクラス( [〜#〜] podo [〜#〜] s)があるとします。

class Customer
{
  int Id;
  String Name;
}

これで、ドメインオブジェクトを次のようにJSONエンコードできるようになりました。

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

残念ながら、これは機能しません...

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)
....

... Dart:convertエンコード方法を明示的に指定しない限り:

class Customer
{
  int Id;
  String Name;

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

モデルクラスのすべてにtoJsonメソッドを本当に追加する必要がありますか、それとももっと良い方法がありますか?

[〜#〜] edit [〜#〜]:これは私が探しているシンプルなシリアル化です:

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

たとえば、 ServiceStack.TextToJsonと比較してください。

Dartの serialization ライブラリ(以下のMatt Bの回答を参照)は、正しい方向への一歩のように思えます。しかし、これは...

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

...値を含む配列(キーなし)を生成します。

[17,"John"]

一方、デフォルトの SimpleMapFormat を使用すると、この complex 表現が生成されます。

探しているものがまだ見つかりません...

EDIT 2:コンテキストの追加:DartでRESTful Webサービスを構築しています。簡単に使用できるJSONシリアル化を探しています別のDartクライアントだけでなく、任意のクライアントによって。たとえば、このまさに質問についてStack Exchange APIにクエリを実行すると、 このJSON応答 が作成されます。これは私が探しているシリアル化形式です。 -または、 Twitter REST API または Facebook Graph API によって返される一般的なJSON応答を確認します。

EDIT 3:これについて小さな ブログ投稿 を書きました。 Hacker Newsの discussion も参照してください。

44
Max

IMOこれはDartの大きな欠点であり、Webアプリケーションに焦点を当てていることを考えれば驚くべきことです。標準ライブラリにJSONサポートがあると、JSONへのクラスとJSONからのクラスのシリアル化が水のように機能することを意味すると思っていましたが、残念なことにJSONサポートは不完全であるように見えますが、選択肢は緩やかに型付けされたマップを操作するか苦しむようです不要なボイラープレートを使用して、標準(PODO)クラスを構成し、期待どおりにシリアル化します。

反射とミラーのサポートなし

Flutter のような一般的なDartプラットフォームはReflection/Mirrorsをサポートしていないため、唯一の選択肢はコード生成ソリューションを使用することです。 ServiceStackの DartおよびFlutterのネイティブサポート で採用したアプローチにより、すべてのServiceStackサービスの型指定されたDartモデルをリモートURLから生成できます。

_$ npm install -g @servicestack/cli

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

.NET Coreおよび 。NETの一般的なホスティングオプション のいずれかでサポートされています。

上記の例では、 www.techstacks.io/types/Dart エンドポイントから生成されたDTOを使用して、 。NET Core 2.0 TechStacksプロジェクト の型付きAPIを生成します。これは、Dartの JsonCodecパターン に従ってモデルを生成します。ここでは、fromJsonという名前のコンストラクターとtoJson()インスタンスメソッドを提供することにより、Dartモデルのシリアル化をカスタマイズできます。生成されたDTOのいずれか:

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

このモデルでは、Dartのbuilt - json:convert APIs を使用して、モデルをJSONにシリアライズおよびデシリアライズできます。例:

_//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);
_

このアプローチの利点は、すべてのDartプラットフォームで動作することです。Flutter、AngularDart、またはDart 2のストロングモードの有無にかかわらずDart Webアプリを含みます。

生成されたDTOは、 servicestackのDartパッケージ とともに使用して、型指定されたDTOに対するJSONシリアル化を処理するエンドツーエンド型指定ソリューションを有効にすることもできます。

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

詳細については、 ServiceStackのネイティブDartサポート のドキュメントを参照してください。

鏡付きダーツ

Mirrorsサポートが利用可能なプラットフォームでDartを使用している場合、Mixinを使用することで最小限の労力で済むことがわかりました。

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

}
_

あなたはあなたのPODOクラスとミックスインすることができます:

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

_JSON.encode_で使用できるようになりました:

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

print(JSON.encode(c));
_

結果:

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

注: ミラーの使用に関する注意事項を参照

27
mythz

Exportable ライブラリを作成して、MapやJSONへの変換などを解決しました。これを使用すると、モデル宣言は次のようになります。

import 'package:exportable/exportable.Dart';

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

また、JSONに変換する場合、次のことができます。

String jsonString = customer.toJson();

また、JSON文字列から新しいオブジェクトを簡単に初期化できます。

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

または、代わりに:

Customer customer = new Exportable(Customer, jsonString);

詳細については、 [〜#〜] readme [〜#〜] をご覧ください。

14
Leksat

Redstone mapper は、私が使用した最高のシリアル化ライブラリです。 JsonObjectとExportableには、それらのクラスの一部を拡張する必要があるという欠点があります。 Redstone Mapperを使用すると、このような構造を持つことができます

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

ゲッターとセッターで機能し、@Field()で注釈を付けないことで情報を非表示にできます。jsonとの間でフィールドの名前を変更したり、オブジェクトをネストしたり、サーバーとクライアントで機能したりできます。また、Redstone Serverフレームワークと統合され、MongoDBにエンコード/デコードするヘルパーがあります。

私が正しい方向に見た唯一の他のフレームワークは Dartson ですが、Redstone Mapperと比べてまだいくつかの機能が欠けています。

7
Cristian Garcia

別の方法は、 シリアル化パッケージ を使用して、クラスのルールを追加することです。最も基本的なフォームでは、リフレクションを使用してプロパティを自動的に取得します。

6
Matt B

この問題を解決する別のパッケージはbuilt_valueです:

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

Built_valueを使用すると、モデルクラスは次のようになります。

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

Built_valueは単なるシリアル化ではなく、operator ==、hashCode、toString、およびビルダークラスも提供することに注意してください。

3
David Morgan

私は解決しました:

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

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

しかし、私の目標は、モデルクラスとの間でデータをバインドすることです。このソリューションは、モデルクラスを変更できる場合に機能しますが、対照的に、ソリューション「外部」を使用してモデルクラスを変換する必要があります。

参照: DartのJsonObjectライブラリを使用したJSONリストの解析

3
Domenico Monaco

dartson を使用しましたが、とても簡単で馴染みがあります(Javaから来た場合)

0
Moshe Shaham