API + JSON 変換 + モデルクラス

JSON(外国語) → Dartオブジェクト(日本語)
JSONをDartオブジェクトに変換、データを扱いやすくする方法

目次

JSON って何?

JSON (JavaScript Object Notation) は、データをやり取りする形式

{
  "id": 1,
  "name": "田中太郎",
  "fee": 8000,
  "isPaid": true
}
  • { } で囲まれている
  • “キー”: 値 の形式
  • 文字列は ” で囲む
  • 数値・真偽値は囲まない

基本の変換

JSON → Dart(デコード):

import 'dart:convert';

// JSON文字列
String jsonString = '{"name": "田中太郎", "age": 25}';

// Map に変換
Map<String, dynamic> data = json.decode(jsonString);

print(data['name']);  // 田中太郎
print(data['age']);   // 25

Dart → JSON (エンコード):

// Map
Map<String, dynamic> data = {
  'name': '田中太郎',
  'age': 25,
};

// JSON文字列に変換
String jsonString = json.encode(data);

print(jsonString);  // {"name":"田中太郎","age":25}

問題点: Map は使いにくい

// ❌ タイポしやすい
print(data['nmae']);  // null (間違えてもエラーにならない)

// ❌ 型がわからない
var age = data['age'];  // dynamic型

// ❌ 補完が効かない
data['']  // ← IDEが候補を出してくれない

解決策: モデルクラスを作る!

モデルクラスとは?

モデルクラスは、JSONデータをDartクラスに変換したもの!
基本形:

class Student {
  final int id;
  final String name;
  final int fee;
  final bool isPaid;
  
  // ========================================
  // コンストラクタ
  // ========================================
  Student({
    required this.id,
    required this.name,
    required this.fee,
    required this.isPaid,
  });
  
  // ========================================
  // JSON → Student (fromJson)
  // ========================================
  factory Student.fromJson(Map<String, dynamic> json) {
    return Student(
      id: json['id'],
      name: json['name'],
      fee: json['fee'],
      isPaid: json['isPaid'],
    );
  }
  
  // ========================================
  // Student → JSON (toJson)
  // ========================================
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'fee': fee,
      'isPaid': isPaid,
    };
  }
}

使い方:

// JSON文字列
String jsonString = '''
{
  "id": 1,
  "name": "田中太郎",
  "fee": 8000,
  "isPaid": true
}
''';

// JSON → Map
Map<String, dynamic> json = jsonDecode(jsonString);

// Map → Student
Student student = Student.fromJson(json);

// ✅ タイプセーフ!
print(student.name);    // 田中太郎
print(student.fee);     // 8000
print(student.isPaid);  // true

// ✅ 補完が効く!
student.  // ← IDEが候補を出してくれる!

// Student → JSON
Map<String, dynamic> data = student.toJson();
String output = json.encode(data);

リストの扱い

JSON配列:

[
  {"id": 1, "name": "田中太郎", "fee": 8000, "isPaid": true},
  {"id": 2, "name": "佐藤花子", "fee": 8000, "isPaid": false},
  {"id": 3, "name": "鈴木一郎", "fee": 9000, "isPaid": true}
]

変換:

// JSON配列 → List<Student>
List<Student> parseStudents(String jsonString) {
  final List<dynamic> jsonList = json.decode(jsonString);
  
  return jsonList.map((json) => Student.fromJson(json)).toList();
}

// 使い方
String jsonString = '''
[
  {"id": 1, "name": "田中太郎", "fee": 8000, "isPaid": true},
  {"id": 2, "name": "佐藤花子", "fee": 8000, "isPaid": false}
]
''';

List<Student> students = parseStudents(jsonString);

for (var student in students) {
  print(student.name);
}

API から取得:

Future<List<Student>> fetchStudents() async {
  final url = Uri.parse('https://api.example.com/students');
  final response = await http.get(url);
  
  if (response.statusCode == 200) {
    // JSON配列をデコード
    final List<dynamic> jsonList = json.decode(response.body);
    
    // List<Student> に変換
    return jsonList.map((json) => Student.fromJson(json)).toList();
  } else {
    throw Exception('Failed to load students');
  }
}

// 使い方
final students = await fetchStudents();
print('生徒数: ${students.length}');

便利なツール

  1. quicktype.io ⭐⭐⭐⭐⭐
    JSONを貼り付けると、自動でモデルクラスを生成!

https://app.quicktype.io にアクセス
左にJSON貼り付け
右上で「Dart」を選択
右側にコードが生成される!

超便利! 😊

  1. json_serializable パッケージ
    自動でfromJson/toJsonを生成!
dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  build_runner: ^2.4.0
  json_serializable: ^6.7.0
import 'package:json_annotation/json_annotation.dart';

part 'student.g.dart';  // 自動生成されるファイル

@JsonSerializable()
class Student {
  final int id;
  final String name;
  final int fee;
  final bool isPaid;
  
  Student({
    required this.id,
    required this.name,
    required this.fee,
    required this.isPaid,
  });
  
  // ========================================
  // 自動生成される
  // ========================================
  factory Student.fromJson(Map<String, dynamic> json) =>
      _$StudentFromJson(json);
  
  Map<String, dynamic> toJson() => _$StudentToJson(this);
}

flutter pub run build_runner buildで
student.g.dart が自動生成される!

コツを調べてみた

as でキャスト

// ✅ 安全
factory Student.fromJson(Map<String, dynamic> json) {
  return Student(
    id: json['id'] as int,        // int にキャスト
    name: json['name'] as String,  // String にキャスト
    fee: json['fee'] as int,
  );
}

// ❌ 危険
factory Student.fromJson(Map<String, dynamic> json) {
  return Student(
    id: json['id'],    // dynamic型のまま
    name: json['name'],
    fee: json['fee'],
  );
}

null チェック

factory Student.fromJson(Map<String, dynamic> json) {
  return Student(
    id: json['id'] as int,
    name: json['name'] as String,
    fee: json['fee'] as int,
    
    // null許容フィールド
    paidDate: json['paidDate'] != null
        ? DateTime.parse(json['paidDate'])
        : null,
    
    // デフォルト値
    photoPath: json['photoPath'] as String? ?? '',
    
    // リスト (null の場合は空リスト)
    lessonRecords: (json['lessonRecords'] as List<dynamic>?)
            ?.map((e) => LessonRecord.fromJson(e))
            .toList() ??
        [],
  );
}

エラーハンドリング

factory Student.fromJson(Map<String, dynamic> json) {
  try {
    return Student(
      id: json['id'] as int,
      name: json['name'] as String,
      fee: json['fee'] as int,
      isPaid: json['isPaid'] as bool,
    );
  } catch (e) {
    print('JSONパースエラー: $e');
    print('JSON: $json');
    rethrow;  // エラーを再スロー
  }
}

フォルダ構成

lib/
  models/
    student.dart
    lesson_record.dart
    event.dart
    venue.dart
  services/
    api_service.dart
  screens/
    student_list_page.dart

まとめ

基本の流れ:

  • モデルクラスを作る
  • fromJson メソッド(JSON → オブジェクト)
  • toJson メソッド(オブジェクト → JSON)
  • API からデータ取得
  • fromJson で変換

ツール:

  • quicktype.io: JSONから自動生成
  • json_serializable: コード自動生成

一言まとめ:
モデルクラスでJSONをタイプセーフに!
fromJson で JSON → オブジェクト、
toJson でオブジェクト → JSON。
as でキャスト、null チェック、エラーハンドリングを忘れずに。
quicktype.io で自動生成が超便利!

目次