DevToolBoxFREE
BlogAdvertise

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
この記事は役に立ちましたか?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy