🎃
DartでAPIの値が0やnullになるときの対処法(カスタムJsonConverterの作り方)
🎯 この記事でわかること
- APIから
0
やnull
が返ってくるときに、Dart側でどう処理するか -
@JsonConverter
を使ったカスタムコンバータの作成方法 - DartPadで試せるシンプルなサンプルコード付き!
🧩 背景:APIのレスポンスが0ってどういうこと?
ある日、APIから以下のようなデータが返ってきたとします:
{
"user": 0
}
この user は本来オブジェクト(たとえば {"id": 1, "name": "Alice"}
)が返ってくるべき項目ですが、0
や null
、false
といった値が返されてしまうことがあります。これはAPI側の都合や状態管理の仕様によるもので、サーバー側での統一的な対応が難しい場合もあるため、クライアント側で柔軟に処理する必要があります。
これをそのまま User
クラスにマッピングしようとすると、クラッシュしてしまいます。
💡 解決策:カスタムJsonConverterを使おう
Dartの json_serializable
パッケージには @JsonConverter
という便利な機能があります。
これを使えば、データの変換ルールを自分で書くことができます。
🧪 サンプルコード(DartPad対応)
import 'dart:convert';
/// ユーザーデータを表すクラス
class User {
/// ユーザーID
final int id;
/// ユーザー名
final String name;
/// コンストラクタ(Userクラスのインスタンスを作る)
User({required this.id, required this.name});
/// JSON(Map形式)からUserインスタンスを生成するファクトリーメソッド
///
/// [json] - ユーザー情報のMap(例:{'id': 1, 'name': 'Alice'})
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
);
}
/// UserインスタンスをMap形式のJSONに変換する
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
};
}
/// APIのユーザー情報をUserインスタンスに変換するカスタムコンバータ
class UserConverter {
/// JSONデータからUserインスタンスを生成する
///
/// [json] - ユーザー情報のJSON(Map形式 または 0 / null の場合あり)
///
/// - 通常はMap(例:{'id': 1, 'name': 'Alice'})
/// - 0やnullが入ってくる場合(例:{'user': 0} または {'user': null})はnullを返す
static User? fromJson(dynamic json) {
if (json is Map<String, dynamic>) {
return User.fromJson(json);
}
// jsonがMap以外(0やnull)の場合はnullを返す
return null;
}
/// UserインスタンスをJSON形式に変換する
///
/// [user] - 変換したいUserインスタンス。nullの場合も対応。
static dynamic toJson(User? user) {
return user?.toJson(); // userがnullならnullを返す
}
}
void main() {
// パターン1:正常なユーザー情報が返ってきた場合
const json1 = '{"user": {"id": 1, "name": "Alice"}}';
final map1 = json.decode(json1) as Map<String, dynamic>; // JSON文字列をMapに変換
final user1 = UserConverter.fromJson(map1['user']); // 'user'部分をUserに変換
print('User1: ${user1?.name}'); // => Alice(ユーザー名を出力)
// パターン2:APIから0が返ってきた場合(例:休会中などのケース)
const json2 = '{"user": 0}';
final map2 = json.decode(json2) as Map<String, dynamic>;
final user2 = UserConverter.fromJson(map2['user']);
print('User2: ${user2?.name ?? "null"}'); // => null(nullを出力)
// パターン3:APIからnullが返ってきた場合(ユーザー情報が存在しない)
const json3 = '{"user": null}';
final map3 = json.decode(json3) as Map<String, dynamic>;
final user3 = UserConverter.fromJson(map3['user']);
print('User3: ${user3?.name ?? "null"}'); // => null(nullを出力)
}
🔄 @JsonConverterを使った変換処理
Dartの json_serializable
パッケージには @JsonConverter
という便利な機能があります。
これを使えば、データの変換ルールを自分で書くことができます。
Flutterで json_serializable
を使っている場合は、以下のように @JsonKey
と @JsonConverter
を使います。
import 'package:json_annotation/json_annotation.dart';
part 'sample_response.g.dart';
()
class SampleResponse {
() // ← ここでカスタムコンバータを指定しています!
final User? user;
SampleResponse({this.user});
factory SampleResponse.fromJson(Map<String, dynamic> json) =>
_$SampleResponseFromJson(json);
Map<String, dynamic> toJson() => _$SampleResponseToJson(this);
}
class User {
final int id;
final String name;
User({required this.id, required this.name});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
};
}
/// ★★★★★ ここがカスタムコンバータ! ★★★★★
class UserDtoConverter implements JsonConverter<User?, Object?> {
/// 定数コンストラクタ(アノテーションで使うため)
const UserDtoConverter();
/// JSONからUser?に変換
/// - MapだったらUser.fromJsonを呼ぶ
/// - 0やnullならnullを返す
User? fromJson(Object? json) {
if (json is Map<String, dynamic>) {
return User.fromJson(json);
}
return null;
}
/// User?からJSONに変換(nullならnullを返す)
Object? toJson(User? object) => object?.toJson();
}
✅ まとめ
- APIの仕様によって
0
やnull
が返るケースでは、クラッシュを避けるための変換処理が必要 -
JsonConverter
や独自の変換ロジックを使うことで、安全にデータを取り扱える
🔗 関連リンク
- DartPad: https://dartpad.dev
- JsonConverter公式ドキュメント:
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html
このようなパターンはAPIの設計ミスに近いですが、現場ではよくあります😅
ぜひこのパターンを覚えて、安全で柔軟なデコード処理を実装していきましょう!
Discussion