Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Десериализация JSON в Dart: Полное руководство
Десериализация JSON — одна из самых частых операций в мобильных приложениях. В Dart есть несколько подходов, от встроенных возможностей до специализированных библиотек. Расскажу о каждом.
1. Встроенный подход: jsonDecode
Samый простой способ — использовать встроенную функцию jsonDecode из dart:convert.
import 'dart:convert';
final jsonString = '{"name": "Иван", "age": 28}';
// Десериализация в Map
final Map<String, dynamic> data = jsonDecode(jsonString);
print(data['name']); // Иван
print(data['age']); // 28
Минусы:
- Нет типизации (всё
dynamic) - Ошибки только во время выполнения
- Нужно вручную извлекать поля
2. Класс с методом fromJson (рекомендуемый подход)
Основной паттерн в Dart-разработке — создавать модели с методом fromJson.
class User {
final String name;
final int age;
final String email;
User({
required this.name,
required this.age,
required this.email,
});
// Фабричный конструктор для десериализации
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
email: json['email'] as String,
);
}
// Обратная сериализация
Map<String, dynamic> toJson() => {
'name': name,
'age': age,
'email': email,
};
}
// Использование
final jsonString = '{"name": "Иван", "age": 28, "email": "ivan@example.com"}';
final user = User.fromJson(jsonDecode(jsonString));
print(user.name); // Иван
3. Работа со списками и вложенными объектами
Часто JSON содержит массивы и вложенные структуры.
class Team {
final String name;
final List<User> members;
final DateTime createdAt;
Team({
required this.name,
required this.members,
required this.createdAt,
});
factory Team.fromJson(Map<String, dynamic> json) {
return Team(
name: json['name'] as String,
members: (json['members'] as List<dynamic>)
.map((user) => User.fromJson(user as Map<String, dynamic>))
.toList(),
createdAt: DateTime.parse(json['created_at'] as String),
);
}
Map<String, dynamic> toJson() => {
'name': name,
'members': members.map((m) => m.toJson()).toList(),
'created_at': createdAt.toIso8601String(),
};
}
// Использование
final jsonString = '''{
"name": "Flutter Team",
"members": [
{"name": "Иван", "age": 28, "email": "ivan@example.com"},
{"name": "Мария", "age": 26, "email": "maria@example.com"}
],
"created_at": "2024-01-15T10:30:00Z"
}''';
final team = Team.fromJson(jsonDecode(jsonString));
print(team.name); // Flutter Team
print(team.members.length); // 2
4. Обработка опциональных полей
Не все поля в JSON обязательны.
class Profile {
final String username;
final String? bio; // опциональное
final String? avatarUrl; // опциональное
final int followers;
Profile({
required this.username,
this.bio,
this.avatarUrl,
required this.followers,
});
factory Profile.fromJson(Map<String, dynamic> json) {
return Profile(
username: json['username'] as String,
bio: json['bio'] as String?,
avatarUrl: json['avatar_url'] as String?,
followers: json['followers'] as int? ?? 0, // значение по умолчанию
);
}
}
5. Использование json_serializable (лучший вариант)
Для больших моделей используют кодогенератор json_serializable, который автоматически создаёт методы.
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart'; // Автогенерируемый файл
@JsonSerializable()
class User {
final String name;
final int age;
@JsonKey(name: 'email_address')
final String email; // Может отличаться от JSON ключа
User({
required this.name,
required this.age,
required this.email,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
В pubspec.yaml:
dependencies:
json_annotation: ^4.8.0
dev_dependencies:
build_runner: ^2.4.0
json_serializable: ^6.7.0
Далее запусти:
flutter pub run build_runner build
6. Обработка ошибок при десериализации
Всегда нужна обработка ошибок, так как JSON может быть невалидным.
class ApiResponse<T> {
final T? data;
final String? error;
final bool isSuccess;
ApiResponse.success(this.data)
: error = null,
isSuccess = true;
ApiResponse.error(this.error)
: data = null,
isSuccess = false;
}
ApiResponse<User> parseUserResponse(String jsonString) {
try {
final json = jsonDecode(jsonString) as Map<String, dynamic>;
final user = User.fromJson(json);
return ApiResponse.success(user);
} on FormatException catch (e) {
return ApiResponse.error('Invalid JSON: ${e.message}');
} on TypeError catch (e) {
return ApiResponse.error('Type mismatch: ${e.toString()}');
} catch (e) {
return ApiResponse.error('Unknown error: ${e.toString()}');
}
}
7. Практический пример: API Response
class ApiClient {
Future<User> getUser(int id) async {
final response = await http.get(
Uri.parse('https://api.example.com/users/$id'),
);
if (response.statusCode == 200) {
return User.fromJson(
jsonDecode(response.body) as Map<String, dynamic>,
);
} else {
throw Exception('Failed to load user');
}
}
Future<List<User>> getUsers() async {
final response = await http.get(
Uri.parse('https://api.example.com/users'),
);
if (response.statusCode == 200) {
final jsonData = jsonDecode(response.body) as List<dynamic>;
return jsonData
.map((user) => User.fromJson(user as Map<String, dynamic>))
.toList();
} else {
throw Exception('Failed to load users');
}
}
}
8. Сравнение подходов
| Подход | Простота | Типизация | Производительность |
|---|---|---|---|
| jsonDecode | Очень простой | Слабая | Хорошая |
| fromJson вручную | Средняя | Отличная | Отличная |
| json_serializable | Сложная (setup) | Отличная | Отличная |
Рекомендация
- Для маленьких проектов → используй
fromJsonвручную - Для больших проектов → используй
json_serializable - Никогда не работай с
dynamicнапрямую в production коде
Правильная десериализация JSON — основа надёжного мобильного приложения.