DartのパターンマッチでJSONを扱う

2023-10-14#dart

Dart 3で導入されたパターンマッチに慣れるため、JSONのパースを試してみたところ、とても便利で最高だったのでブログに残しておきたい。

パターンマッチを使わない場合

const pokedex = '''[
  {"id": 1, "name": "Bulbasaur", "type": ["Grass", "Poison"]},
  {"id": 4, "name": "Charmander", "type": ["Fire"]},
  {"id": 7, "name": "Squirtle", "type": ["Water"]}
]''';
 
void main() {
  try {
    final array = json.decode(pokedex);
    if (array is List<dynamic>) {
      for (final object in array) {
        if (object is Map<String, dynamic> &&
            object['id'] is int &&
            object['name'] is String &&
            object['type'] is List<dynamic>) {
          print(
              'id: ${object['id']}, name: ${object['name']}, types: ${object['type']}');
        }
      }
    }
  } catch (_) {
    print('failed to decode');
  }
}

要素の型をちゃんとチェックしようとすると、かなり冗長なコードになってしまう。

パターンマッチを使う場合

const pokedex = '''
[
  {"id": 1, "name": "Bulbasaur", "type": ["Grass", "Poison"]},
  {"id": 4, "name": "Charmander", "type": ["Fire"]},
  {"id": 7, "name": "Squirtle", "type": ["Water"]}
]
''';
 
void main() {
  if (json.decode(pokedex) case List<dynamic> array) {
    for (final {
          'id': int id,
          'name': String name,
          'type': List<dynamic> types,
        } in array) {
      print('id: $id, name: $name, types: $types');
    }
  } else {
    print('failed to decode');
  }
}

型チェックと同時に変数へのbindをおこなっているため、簡潔でわかりやすいコードになった。Mapの要素に対する型チェックもかなり簡潔になっている。パターンマッチのポイントは以下のとおり。