[Flutter] JSONファイルの扱い方

## 1. はじめに

Dart公式の`json_annotation`ライブラリを利用し、Flutterアプリで簡単にJSONファイルを扱える方法についてまとめています。

### 利用するライブラリ一覧

 - [json_annotation](https://pub.dev/packages/json_annotation)
 - [build_runner](https://pub.dev/packages/build_runner)
 - [json_serializable](https://pub.dev/packages/json_serializable)

## 2. JSONファイル

説明用に作成したJSONファイルです。今回はリスト型のネストされた (入れ子) 構造にしてみました。

```json:assets/sample.json
{
    "list": [
        {
            "id": "0",
            "name": "taro"
        },
        {
            "id": "1",
            "name": "jiro"
        }
    ]
}
```

このJSONファイルをパースして中身をそのまま表示するだけのコードをサンプルとして説明します。
![スクリーンショット 2020-08-27 18.35.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259963/9378ca41-f181-3a13-0b10-eb856dc20f3a.png)


## 3. pubspec.yamlにライブラリを追加

2020/8/27時点の最新バージョンです。

```yaml:pubspec.yaml
dependencies:
  json_annotation: ^3.0.1

dev_dependencies:
  build_runner: ^1.10.2
  json_serializable: ^3.4.1
```

## 4. モデルの作成

JSONファイルをシリアライズ/デシリアライズするためのモデルを作成します。

上記で示したJSONファイルに対応したサンプルコードを以下に示します。今回は、ネスト構造のJSONファイルにしているため、モデル自体も2つ必要になります。

なお、このソースコードを作成した時点ではエラーでビルドは通りません。後述のツールを用いて`sample.g.dart`ファイルを自動生成する必要があったりするので、深い事は考えずにテンプレートとして用いましょう。`fromJson`, `toJson`メソットでシリアライズ, デシリアライズします。

`JsonSerializable`の仕様については[こちら](https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable-class.html)を参照して下さい。

```Dart:model/sample.dart
import 'package:json_annotation/json_annotation.dart';
part 'sample.g.dart'; // これは後述の手順で自動生成されるソースコードです！

@JsonSerializable()
class SampleModel {
  SampleModel({this.id, this.name});
  String id;
  String name;

  factory SampleModel.fromJson(Map<String, dynamic> json) =>
      _$SampleModelFromJson(json);

  Map<String, dynamic> toJson() => _$SampleModelToJson(this);
}

@JsonSerializable(explicitToJson: true)
class Samples {
  List<SampleModel> list = [];
  Samples(this.list);

  factory Samples.fromJson(Map<String, dynamic> json) =>
      _$SamplesFromJson(json);

  Map<String, dynamic> toJson() => _$SamplesToJson(this);
}
```

## 5. `*.g.dart`ファイルの自動生成

`build_runner`ライブラリを利用して、`sample.g.dart`ファイルを自動生成します。

```shell
$ flutter packages pub run build_runner build
```

## 6. JSONファイルをパースしてモデルに格納

`Samples.fromJson(jsonDecode(JSONファイルのパス))`みたいな感じで、JSONファイルをオープン＆パースし、結果がリターンされます。簡単ですね。

```Dart:main.dart
class _MyHomePageState extends State<MyHomePage> {
  Samples _samples;

  @override
  void initState() {
    super.initState();
    _jsonFileLoad().then((value) {
      setState(() {
        _samples = value;
        assert(_samples.list.isNotEmpty);
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: _samples == null
              ? List<Widget>()
              : <Widget>[
                  for (int i = 0; i < _samples.list.length; i++)
                    Text(
                      'ID: ${_samples.list[i].id}, NAME: ${_samples.list[i].name}',
                    ),
                ],
        ),
      ),
    );
  }

  Future<dynamic> _jsonFileLoad() async {
    String path = 'assets/sample.json';
    String jsonString;

    try {
      jsonString = await rootBundle.loadString(path);
    } on FlutterError {
      print('Faild to open $path');
      exit(0);
    }

    return Samples.fromJson(jsonDecode(jsonString));
  }
```

## 7. 参考文献

- [JSON Serialization in Dart ~json_serializableの巻~](https://qiita.com/kitoko552/items/81168294f5738175ff1d)

## 8. サンプルプロジェクト一式

今回説明したサンプルのプロジェクトは[github](https://github.com/Kurun-pan/flutter_json_example)に置いています。