最近ぽちぽちされるようになったFlutterの画面遷移のサンプルソースを確認したところ、dartsonが使えなくなっており、json_serializableに差し替えたので、その手順をまとめました。
他にも古いFlutterアプリを最新の環境でビルドして実行させるためにいくつか更新する必要がありましたが、それはまた別途まとめたいと思います。
##dartson
dartsonは開発中においてはmirrors(リフレクション)を使用しているが、deploy時には不要なのでdart2jsにやさしい、ということでちょっと前まで推されてました。
https://www.dartlang.org/articles/libraries/serialization
実際、使ってみると非常にシンプルで使い勝手がよかったと思います。
flutterは前々からmirrorsをサポートしていなかったとは思いますが、そのときは何も意識しておらず、単に使えていたから使ったにすぎないのですが、Flutter Beta 2の現時点(実際にはもっと前からだと思いますが未確認)で、正常には動作できなくなっています。
E/flutter ( 4126): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] error: import of dart:mirrors with --enable-mirrors=false
E/flutter ( 4126): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Dart_GetClosure expects argument 'library' to be non-null.
確かに、gitterのFlutterチャンネルで、Flutterの中の人がほかのパッケージを探せといってます。
Michael Thomsen @mit-mit Dec 22 2017 16:28
Yes, mirrors are not supported so if the library actually depends on them, it wont work
Michael Thomsen @mit-mit Dec 22 2017 16:31
I have looked for this specifically before, but generally options would be 1) look for another dart package, 2) write a dart package, or 3) write a small plugin that then does it on the platform side
##json_serializable
jsonパッケージはいくつかあるようですが、Dartは"Batteries included"なので、まずはDartチーム謹製を試すのが正しい姿勢かと思い、json_serializableに差し替えてみました。
なお、Flutter視点で書いてますが、参考にさせて頂いたAngularDartでHTTP(REST)通信の通り、json_serializableはFlutterに限らず使用できます。
但し、ここ数ヶ月でコード生成手順が変わってます。めまぐるしい、、、。
###パッケージ追加
dependencies:
# ...snip...
json_annotation: ^0.2.3
dev_dependencies:
# ...snip...
build_runner: ^0.8.0
json_serializable: ^0.5.0
###モデルクラス作成
import 'package:json_annotation/json_annotation.dart';
part 'library.g.dart';
@JsonSerializable()
class Library extends Object with _$LibrarySerializerMixin {
@JsonKey(name: 'systemid')
final String systemId; // システムID
@JsonKey(name: 'systemname')
final String systemName; // システム名称
@JsonKey(name: 'libkey')
final String libKey; // システム毎の図書館キー
@JsonKey(name: 'libid')
final String libId; // 図書館のユニークID
@JsonKey(name: 'short')
final String shortName; // 略称
@JsonKey(name: 'formal')
final String formalName; // 正式名称
@JsonKey(name: 'url_pc')
final String urlPC; // PC版ウェブサイト
final String address; // 住所
final String pref; // 都道府県
final String city; // 市町村
final String post; // 郵便番号
final String tel; // 電話番号
@JsonKey(name: 'geocode')
final String geoCode; // 位置情報
final String category; // カテゴリー(下記参照)
final String image; // 外観写真(現時点では空です)
final String distance; // パラメータでgeocodeが指定されている場合、その地点からの距離(単位:km)
Library(
{this.systemId,
this.systemName,
this.libKey,
this.libId,
this.shortName,
this.formalName,
this.urlPC,
this.address,
this.pref,
this.city,
this.post,
this.tel,
this.geoCode,
this.category,
this.image,
this.distance});
factory Library.fromJson(Map<String, dynamic> json) => _$LibraryFromJson(json);
}
一部これから生成して参照する'クラスのファイル名.g.dart'のコードをpartで指定します。_$で参照しているのはこれから生成される名前です。当然ですが、この時点では存在していないので、IDEを使用しているとエラーで表示されます。
クラスは'_$クラス名SerializerMixin'をmixinする決まりです。
class Library extends Object with _$LibrarySerializerMixin {
factoryコンストラクタは'_$クラス名FromJson'ファンクションで生成する決まりです。
factory Library.fromJson(Map<String, dynamic> json) => _$LibraryFromJson(json);
アノテーションはjson_annotationパッケージに依存しています。exampleのほうが参考になるかも。だいたい見たまんまですがドキュメントはこちら。
###コード生成
コマンドを実行し、'クラスのファイル名.g.dart'ファイルを生成します。
$ flutter packages pub run build_runner build
ちなみに、これはflutterからなので、通常のコマンドはpub run build_runner build
になります。よくみればDart 2におけるただのbuildコマンドなので、安定版がリリースされる頃には手打ちせずともウラで勝手に走っていると思います。(←build_runnerがマストというわけでもなさそうなのでガセだったかな。JetBrains系IDEはWatcherで対応するとか?)
watcherを起動しておけば、ソースの更新を監視して勝手にbuildしてくれます。
$ flutter packages pub run build_runner watch
以下のファイルが同じディレクトリに生成されます。
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'library.dart';
// **************************************************************************
// Generator: JsonSerializableGenerator
// **************************************************************************
Library _$LibraryFromJson(Map<String, dynamic> json) => new Library(
systemId: json['systemid'] as String,
...snip...
distance: json['distance'] as String);
abstract class _$LibrarySerializerMixin {
String get systemId;
...snip...
String get distance;
Map<String, dynamic> toJson() => <String, dynamic>{
'systemid': systemId,
...snip...
'distance': distance
};
}
シリアライズされたJSON文字列からオブジェクトを生成する箇所は通常こんな感じかと。
Library library = new Library.fromJson(json.decode(data) as Map<String, dynamic>);
但し、ここのケースでは配列が戻ってきますので、こんな感じになります。
List<Library> libraries = (json.decode(data) as List).map((e) => new Library.fromJson(e)).toList();
あとはビルドして実行するだけです。
##参照
https://flutter.io/json/
AngularDartでHTTP(REST)通信
JSON serialization in Dart strong mode