DevToolBox無料
ブログ

JSON to Dart: Flutter モデルクラス完全ガイド

10分by DevToolBox

JSONをDartモデルクラスに変換することは、Flutter開発で最も一般的なタスクの一つです。REST APIからデータを取得する場合でも、ローカル設定を読み取る場合でも、JSONデータを安全に解析して使用するためには、適切に構造化されたDartクラスが必要です。

モデルクラスが必要な理由

  • 型安全性 — ランタイムではなくコンパイル時にエラーをキャッチ
  • IDEの自動補完とリファクタリングサポート
  • フロントエンドとバックエンド間の明確なデータ契約
  • テストとメンテナンスが容易に

基本的なJSONからDartへの型マッピング

JSON型Dart型
stringString"hello" → "hello"
number (int)int42 → 42
number (float)double3.14 → 3.14
booleanbooltrue → true
nullNull / dynamicnull → null
arrayList<T>[1,2,3] → [1,2,3]
objectMap / Class{} → User()

手動モデルクラス(fromJson / toJson)

最も簡単なアプローチは、モデルクラスを手動で書くことです。完全な例:

// JSON input
{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com",
  "isActive": true,
  "score": 95.5
}

// Dart model class
class User {
  final int id;
  final String name;
  final String email;
  final bool isActive;
  final double score;

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

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      email: json['email'] as String,
      isActive: json['isActive'] as bool,
      score: (json['score'] as num).toDouble(),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
      'isActive': isActive,
      'score': score,
    };
  }
}

ネストされたオブジェクトの処理

JSONにネストされたオブジェクトが含まれる場合、各レベルに個別のDartクラスを作成します:

// JSON with nested object
{
  "id": 1,
  "name": "John",
  "address": {
    "street": "123 Main St",
    "city": "Springfield",
    "zipCode": "62701"
  }
}

// Dart classes
class Address {
  final String street;
  final String city;
  final String zipCode;

  Address({required this.street, required this.city, required this.zipCode});

  factory Address.fromJson(Map<String, dynamic> json) {
    return Address(
      street: json['street'] as String,
      city: json['city'] as String,
      zipCode: json['zipCode'] as String,
    );
  }

  Map<String, dynamic> toJson() => {
    'street': street, 'city': city, 'zipCode': zipCode,
  };
}

class User {
  final int id;
  final String name;
  final Address address;

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

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      address: Address.fromJson(json['address'] as Map<String, dynamic>),
    );
  }

  Map<String, dynamic> toJson() => {
    'id': id, 'name': name, 'address': address.toJson(),
  };
}

リストと配列の処理

JSON配列はDartのList型にマッピングされます:

// JSON with arrays
{
  "name": "John",
  "hobbies": ["coding", "reading", "gaming"],
  "orders": [
    {"id": 1, "product": "Laptop", "price": 999.99},
    {"id": 2, "product": "Mouse", "price": 29.99}
  ]
}

// Dart
class User {
  final String name;
  final List<String> hobbies;
  final List<Order> orders;

  User({required this.name, required this.hobbies, required this.orders});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'] as String,
      hobbies: List<String>.from(json['hobbies'] as List),
      orders: (json['orders'] as List)
          .map((e) => Order.fromJson(e as Map<String, dynamic>))
          .toList(),
    );
  }
}

Null Safetyのベストプラクティス

Dartのサウンドnull safetyでは、nullable フィールドを適切に処理する必要があります:

  • 必ず存在するフィールドには `required` キーワードを使用
  • nullable型には `?` サフィックスを使用(例: `String?`)
  • fromJsonで `??` 演算子でデフォルト値を提供
  • 値が初期化されることが確実な場合のみ `late` を使用
class User {
  final int id;
  final String name;
  final String? bio;        // nullable
  final String avatarUrl;   // with default

  User({
    required this.id,
    required this.name,
    this.bio,
    this.avatarUrl = 'https://example.com/default-avatar.png',
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      bio: json['bio'] as String?,
      avatarUrl: json['avatarUrl'] as String? ?? 'https://example.com/default-avatar.png',
    );
  }
}

json_serializableパッケージの使用

大規模プロジェクトでは、json_serializableパッケージがボイラープレートを自動化します:

ステップ1: 依存関係を追加

# pubspec.yaml
dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  json_serializable: ^6.7.1
  build_runner: ^2.4.6

ステップ2: アノテーション付きモデルを作成

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  final int id;
  final String name;
  final String email;

  @JsonKey(name: 'is_active')
  final bool isActive;

  @JsonKey(defaultValue: 0.0)
  final double score;

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

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

ステップ3: Build Runnerを実行

dart run build_runner build --delete-conflicting-outputs

Freezedで不変モデルを作成

freezedパッケージはcopyWith、等価性、JSON シリアライゼーション付きの不変クラスを生成します:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {
  const factory User({
    required int id,
    required String name,
    required String email,
    @Default(false) bool isActive,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

よくある間違いと修正方法

間違い修正
nullチェックなしで `json['key']` を使用`json['key'] as String? ?? ''` を使用するかnullを処理
ネストされたオブジェクトの変換を忘れるオブジェクトには `NestedClass.fromJson(json['key'])` を呼ぶ
すべての数値に `int` を使用`num` を使い変換: `(json['price'] as num).toDouble()`
欠けているキーを処理しない`json.containsKey('key')` を使うかデフォルト値を提供
toJsonで誤った型を返すネストされたオブジェクトが自身の `.toJson()` を呼ぶことを確認

よくある質問

JSONをDartクラスに変換する最良の方法は?

小規模プロジェクトでは手動変換が有効です。大規模プロジェクトではjson_serializableまたはfreezedパッケージでボイラープレートコードを自動生成します。

Dartで動的なJSONキーをどう処理しますか?

未知のキーを持つオブジェクトにはMap<String, dynamic>を使用します。

json_serializableとfreezedのどちらを使うべき?

シンプルなJSONシリアライゼーションにはjson_serializable。不変性、copyWith、union型も必要ならfreezedを使います。

JSON to Dart変換で日付をどう処理しますか?

DateTime.parse(json['date'])でISO 8601文字列を解析します。toJsonではdateTime.toIso8601String()を使います。

𝕏 Twitterin LinkedIn
この記事は役に立ちましたか?

最新情報を受け取る

毎週の開発ヒントと新ツール情報。

スパムなし。いつでも解除可能。

Try These Related Tools

DTJSON to Dart{ }JSON FormatterKTJSON to KotlinTSJSON to TypeScript

Related Articles

YAML構文と検証:よくあるエラーと修正方法

YAML構文をマスター:インデントルール、パースエラー、データ型、ベストプラクティス。

JSONからKotlinデータクラス変換:kotlinx.serialization、Moshi、Gsonガイド

JSONをKotlinデータクラスにオンラインで変換。kotlinx.serialization、Moshi、Gsonを使用したJSON解析を解説。