Flutter初心者の学習記録 第8回:FlutterでのJSON処理マスターガイド

こんにちは、ジミーです!今回は、モバイルアプリ開発において避けて通れない重要なトピック「JSON処理」について深掘りしていきましょう。
APIとの連携が当たり前となった現代のアプリ開発において、JSONデータの取り扱いは基本中の基本スキルです。この記事では、FlutterでのシンプルなJSON処理から、複雑なネストされたJSONの扱い方まで、実践的なコード例と共に解説していきます。
JSONとは?
まずは基本から。JSONは「JavaScript Object Notation」の略で、軽量なデータ交換フォーマットです。人間が読み書きしやすく、マシンにとっても解析・生成が容易という特徴があります。
JSONの基本構造は以下の通りです:
- オブジェクト:
{ }
で囲まれ、"キー": 値
のペアで構成 - 配列:
[ ]
で囲まれ、カンマで区切られた値のリスト - 値: 文字列、数値、真偽値、null、オブジェクト、配列
例えば、以下のようなJSONがあります:
{
"name": "田中太郎",
"age": 28,
"isStudent": false,
"hobbies": ["読書", "旅行", "プログラミング"],
"address": {
"city": "東京",
"zipCode": "123-4567"
}
}
FlutterでのJSON処理の基本
Flutterでは、主に以下の方法でJSONを処理します:
dart:convert
パッケージを使った手動変換- モデルクラスを用いた自動変換
まずは、dart:convert
パッケージによる基本的な処理方法を見ていきましょう。
1. JSONのエンコード・デコード
import 'dart:convert';
void main() {
// JSONからDartのMap型への変換(デコード)
String jsonString = '{"name": "田中太郎", "age": 28}';
Map<String, dynamic> userData = jsonDecode(jsonString);
print(userData['name']); // 田中太郎
print(userData['age']); // 28
// DartのMap型からJSONへの変換(エンコード)
Map<String, dynamic> newUser = {
'name': '佐藤花子',
'age': 25,
'isStudent': true
};
String newJsonString = jsonEncode(newUser);
print(newJsonString); // {"name":"佐藤花子","age":25,"isStudent":true}
}
これが最も基本的なJSON処理です。しかし、実際のアプリ開発では、型の安全性やコードの保守性のために、JSONデータをモデルクラスに変換して扱うことが推奨されています。
モデルクラスを使ったJSON処理
より実践的なアプローチとして、JSONデータをモデルクラスに変換する方法を見てみましょう。
1. 手動でモデルクラスを作成する方法
class User {
final String name;
final int age;
final bool isStudent;
final List<String> hobbies;
final Address address;
User({
required this.name,
required this.age,
required this.isStudent,
required this.hobbies,
required this.address,
});
// JSONからUserインスタンスを生成するファクトリメソッド
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'],
age: json['age'],
isStudent: json['isStudent'],
hobbies: List<String>.from(json['hobbies']),
address: Address.fromJson(json['address']),
);
}
// UserインスタンスからJSONに変換するメソッド
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
'isStudent': isStudent,
'hobbies': hobbies,
'address': address.toJson(),
};
}
}
class Address {
final String city;
final String zipCode;
Address({required this.city, required this.zipCode});
factory Address.fromJson(Map<String, dynamic> json) {
return Address(
city: json['city'],
zipCode: json['zipCode'],
);
}
Map<String, dynamic> toJson() {
return {
'city': city,
'zipCode': zipCode,
};
}
}
このモデルクラスを使って、JSONとの相互変換を行います:
void main() {
String jsonString = '''
{
"name": "田中太郎",
"age": 28,
"isStudent": false,
"hobbies": ["読書", "旅行", "プログラミング"],
"address": {
"city": "東京",
"zipCode": "123-4567"
}
}
''';
// JSONからUserインスタンスへ変換
Map<String, dynamic> jsonMap = jsonDecode(jsonString);
User user = User.fromJson(jsonMap);
print(user.name); // 田中太郎
print(user.address.city); // 東京
// UserインスタンスからJSONへ変換
Map<String, dynamic> userJson = user.toJson();
String newJsonString = jsonEncode(userJson);
print(newJsonString);
}
手動でモデルクラスを作成する方法は、少ないフィールド数であれば問題ありませんが、複雑なJSONになると記述が冗長になります。そこで便利なのが、自動生成ツールの活用です。
2. json_serializableパッケージを使った自動生成
複雑なJSONを扱う場合は、json_serializable
パッケージを使用すると便利です。以下のステップで導入します:
- 必要なパッケージを
pubspec.yaml
に追加:
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.6
json_serializable: ^6.7.1
- モデルクラスを定義:
import 'package:json_annotation/json_annotation.dart';
// この行が必要です
part 'user.g.dart';
@JsonSerializable(explicitToJson: true)
class User {
final String name;
final int age;
final bool isStudent;
final List<String> hobbies;
final Address address;
User({
required this.name,
required this.age,
required this.isStudent,
required this.hobbies,
required this.address,
});
// これだけで自動生成されます
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
@JsonSerializable()
class Address {
final String city;
final String zipCode;
Address({required this.city, required this.zipCode});
factory Address.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);
Map<String, dynamic> toJson() => _$AddressToJson(this);
}
- コード生成を実行:
flutter pub run build_runner build
この一連の流れで、JSONシリアライズ・デシリアライズのためのコードが自動生成されます。
実践例:RESTful APIからのデータ取得
最後に、実際のアプリ開発で頻繁に行われる、APIからのJSONデータ取得と処理の例を見てみましょう:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
// ユーザーモデル
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
);
}
}
class ApiExample extends StatefulWidget {
@override
_ApiExampleState createState() => _ApiExampleState();
}
class _ApiExampleState extends State<ApiExample> {
late Future<List<User>> futureUsers;
@override
void initState() {
super.initState();
futureUsers = fetchUsers();
}
Future<List<User>> fetchUsers() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (response.statusCode == 200) {
List jsonResponse = jsonDecode(response.body);
return jsonResponse.map((data) => User.fromJson(data)).toList();
} else {
throw Exception('Failed to load users');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('API サンプル'),
),
body: Center(
child: FutureBuilder<List<User>>(
future: futureUsers,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data![index].name),
subtitle: Text(snapshot.data![index].email),
);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return CircularProgressIndicator();
},
),
),
);
}
}
JSONデータ処理のベストプラクティス
最後に、FlutterでのJSON処理におけるベストプラクティスをまとめます:
- 型安全性を確保する:可能な限り
dynamic
型の使用を避け、強い型付けを行いましょう - null安全性に注意する:JSONデータにフィールドが存在しない場合の対応を考慮しましょう
- エラーハンドリング:JSONデコード時のエラーを適切に処理しましょう
- 大規模プロジェクトでは自動生成を活用:
json_serializable
などのツールを使用することで、ミスを減らし保守性を高めましょう - テストを書く:JSON変換のテストを書くことで、予期せぬバグを防ぎましょう
まとめ
FlutterでのJSON処理は、アプリ開発の基本スキルの一つです。基本的なdart:convert
パッケージの使用から、モデルクラスを活用した型安全な実装、さらにはjson_serializable
を使った効率的なコード生成まで、さまざまなアプローチがあります。
プロジェクトの規模や複雑さに応じて、最適な方法を選択してください。特に大規模なプロジェクトでは、自動生成ツールを活用することで、開発効率と保守性を高めることができます。
ご質問やコメントがありましたら、下のコメント欄にお気軽にどうぞ。それでは、楽しいFlutter開発を!