mcpField
F
flutter-implement-json-serialization

flutter-implement-json-serialization

github.com/flutter開発3.1k37.8k 回インストール2 日前に更新
リポジトリ

flutter-implement-json-serialization について

Flutter 開発者向けに、モデル変換からネットワーク応答の解析、大きなデータのバックグラウンド処理までを扱う手動 JSON シリアライズのスキルです。

主な機能

  1. 01モデルの fromJson と toJson の実装
  2. 02デコードした JSON の型変換
  3. 03compute による isolate 解析
  4. 04HTTP ステータス確認とエラー処理
  5. 05シリアライズ処理のユニットテスト

活用シーン

  1. 01Dart モデルへの JSON 変換追加
  2. 02HTTP リクエストの応答データ解析
  3. 03大きな JSON ドキュメントのバックグラウンド処理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/flutter/agent-plugins --skill flutter-implement-json-serialization

インストール後、Claude Code で/flutter-implement-json-serializationを入力して読み込まれたか確認します。

詳細

Flutter での手動 JSON シリアライズ

目次

基本ガイドライン

  • dart:convert をインポートする:手動で JSON をエンコード(jsonEncode)およびデコード(jsonDecode)するには、Flutter に組み込まれている dart:convert ライブラリを使用します。
  • 型安全性を徹底する:jsonDecode() の dynamic な戻り値は、必ず想定される型にキャストします。通常、オブジェクトには Map<String, dynamic>、配列には List<dynamic> を使用します。
  • シリアライズロジックをカプセル化する:JSON 構造に対応するプロパティを持つプレーンなモデルクラスを定義します。モデル内に fromJson ファクトリーコンストラクターと toJson メソッドを実装します。
  • バックグラウンドで解析する:大きな JSON ドキュメントを解析する場合(実行時間が 16ms を超える場合)、UI のカクつきを防ぐため、Flutter の compute() 関数を使って解析ロジックを別の isolate に移します。
  • 失敗時に例外をスローする:HTTP レスポンスを処理する際、ステータスコードが成功を示していない場合(例:200 OK または 201 Created ではない場合)は、例外をスローします。null は返さないでください。

ワークフロー:シリアライズ可能なモデルの実装

このチェックリストに従って、データモデルの手動 JSON シリアライズを実装します。

タスクの進捗:

  • final プロパティを持つプレーンなモデルクラスを定義します。
  • factory Model.fromJson(Map<String, dynamic> json) コンストラクターを実装します。
  • Map<String, dynamic> toJson() メソッドを実装します。
  • 両方のシリアライズメソッドのユニットテストを作成します。
  • バリデーターを実行 -> 型の不一致エラーを確認 -> キャストロジックを修正します。
  1. モデルを定義する:JSON キーに対応するプロパティを持つクラスを作成します。
  2. fromJson を実装する:Map から値を取り出し、適切な Dart 型にキャストします。パターンマッチングまたは明示的なキャストを使用します。
  3. toJson を実装する:クラスのプロパティを対応する JSON 文字列キーにマッピングした Map<String, dynamic> を返します。
  4. 検証する:ユニットテストを実行し、型安全性、自動補完、コンパイル時の例外処理が正しく機能することを確認します。

ワークフロー:JSON の取得と解析

ネットワークリクエストから JSON を取得して解析する際は、この条件付きワークフローを使用します。

タスクの進捗:

  • HTTP リクエストを実行します。
  • レスポンスのステータスコードを検証します。
  • 解析方法(同期または isolate)を決定します。
  • JSON をデコードし、モデルにマッピングします。
  1. リクエストを実行する:http パッケージを使用してネットワーク呼び出しを行います。
  2. レスポンスを検証する:
    • response.statusCode == 200(または POST の場合は 201)なら、解析に進みます。
    • ステータスコードが失敗を示す場合は、Exception をスローします。
  3. 解析方法を決定する:
    • 小さなペイロード(例:単一のオブジェクト)を解析する場合は、メインスレッドで同期的に解析します。
    • 大きなペイロード(例:数千個のオブジェクトを含む配列)を解析する場合は、compute(parseFunction, response.body) を使用し、バックグラウンドの isolate で解析します。
  4. デコードしてマッピングする:デコードした JSON をモデルの fromJson コンストラクターに渡します。

例

高精度なモデルの実装
import 'dart:convert';

class User {
  final int id;
  final String name;
  final String email;

  const User({
    required this.id,
    required this.name,
    required this.email,
  });

  // Factory constructor for deserialization
  factory User.fromJson(Map<String, dynamic> json) {
    return switch (json) {
      {
        'id': int id,
        'name': String name,
        'email': String email,
      } => 
        User(
          id: id,
          name: name,
          email: email,
        ),
      _ => throw const FormatException('Failed to load User.'),
    };
  }

  // Method for serialization
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
    };
  }
}
同期解析(小さなペイロード)
import 'dart:convert';
import 'package:http/http.dart' as http;

Future<User> fetchUser(http.Client client, int userId) async {
  final response = await client.get(
    Uri.parse('https://api.example.com/users/$userId'),
    headers: {'Accept': 'application/json'},
  );

  if (response.statusCode == 200) {
    // Decode returns dynamic, cast to Map<String, dynamic>
    final Map<String, dynamic> jsonMap = jsonDecode(response.body) as Map<String, dynamic>;
    return User.fromJson(jsonMap);
  } else {
    throw Exception('Failed to load user');
  }
}
バックグラウンド解析(大きなペイロード)
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;

// Top-level function required for compute()
List<User> parseUsers(String responseBody) {
  final parsed = (jsonDecode(responseBody) as List<dynamic>).cast<Map<String, dynamic>>();
  return parsed.map<User>((json) => User.fromJson(json)).toList();
}

Future<List<User>> fetchUsers(http.Client client) async {
  final response = await client.get(
    Uri.parse('https://api.example.com/users'),
    headers: {'Accept': 'application/json'},
  );

  if (response.statusCode == 200) {
    // Offload expensive parsing to a background isolate
    return compute(parseUsers, response.body);
  } else {
    throw Exception('Failed to load users');
  }
}

コメント