← Назад к вопросам
Как работать с JSON и сериализацией данных во Flutter?
1.6 Junior🔥 302 комментариев
#Работа с сетью#Хранение данных
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
JSON и сериализация данных во Flutter
Сериализация — это фундаментальная часть мобильной разработки. Существует несколько подходов с разными trade-offs.
1. Встроенная сериализация через jsonDecode
import 'dart:convert';
// Простой класс пользователя
class User {
final int id;
final String name;
final String email;
User({
required this.id,
required this.name,
required this.email,
});
// Парсинг из JSON
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
email: json['email'] as String,
);
}
// Конвертация в JSON
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
};
}
}
// Использование
final json = '{"id": 1, "name": "John", "email": "john@example.com"}';
final jsonData = jsonDecode(json);
final user = User.fromJson(jsonData);
// Вызвать на API
http.post(
Uri.parse('https://api.example.com/users'),
body: jsonEncode(user.toJson()),
);
2. Code Generation с json_serializable
Это самый популярный подход в production приложениях:
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: 'created_at') // Маппинг если ключ другой
final DateTime createdAt;
User({
required this.id,
required this.name,
required this.email,
required this.createdAt,
});
// Это генерируется автоматически
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
Выполнить генерацию:
flutter pub run build_runner build
flutter pub run build_runner watch # Автоматически при изменении
3. Сложные типы и nested объекты
@JsonSerializable()
class Post {
final int id;
final String title;
final User author; // Nested объект
final List<Comment> comments; // Список объектов
Post({
required this.id,
required this.title,
required this.author,
required this.comments,
});
factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
Map<String, dynamic> toJson() => _$PostToJson(this);
}
@JsonSerializable()
class Comment {
final int id;
final String text;
final User author;
Comment({
required this.id,
required this.text,
required this.author,
});
factory Comment.fromJson(Map<String, dynamic> json) => _$CommentFromJson(json);
Map<String, dynamic> toJson() => _$CommentToJson(this);
}
// Использование
final jsonStr = '''{"id": 1, "title": "Hello", "author": {"id": 1, "name": "John", "email": "john@example.com"}, "comments": [{"id": 1, "text": "Great!", "author": {...}}]}''';
final post = Post.fromJson(jsonDecode(jsonStr));
4. Custom serialization с fromJsonT
@JsonSerializable()
class Event {
final int id;
final String name;
@JsonKey(fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime datetime;
Event({
required this.id,
required this.name,
required this.datetime,
});
factory Event.fromJson(Map<String, dynamic> json) => _$EventFromJson(json);
Map<String, dynamic> toJson() => _$EventToJson(this);
}
// Кастомная десериализация
DateTime _dateTimeFromJson(String? dateStr) {
if (dateStr == null) throw FormatException('Null datetime');
return DateTime.parse(dateStr);
}
String _dateTimeToJson(DateTime dateTime) {
return dateTime.toIso8601String();
}
5. Обработка ошибок сериализации
User? parseUserSafely(String jsonStr) {
try {
final jsonData = jsonDecode(jsonStr);
return User.fromJson(jsonData);
} on FormatException catch (e) {
print('Invalid JSON: $e');
return null;
} on TypeError catch (e) {
print('Type mismatch: $e');
return null;
}
}
// Или с Result тип
Future<Result<User>> parseUserAsync(String jsonStr) async {
try {
// Тяжёлая обработка в отдельном потоке
final user = await compute(_parseUser, jsonStr);
return Result.success(user);
} catch (e) {
return Result.failure(e.toString());
}
}
User _parseUser(String jsonStr) {
final jsonData = jsonDecode(jsonStr);
return User.fromJson(jsonData);
}
6. Работа с null-safety
@JsonSerializable()
class UserProfile {
final int id;
final String name;
final String? bio; // Nullable
final List<String>? tags; // Nullable список
final DateTime? updatedAt; // Опциональная дата
UserProfile({
required this.id,
required this.name,
this.bio,
this.tags,
this.updatedAt,
});
factory UserProfile.fromJson(Map<String, dynamic> json) => _$UserProfileFromJson(json);
Map<String, dynamic> toJson() => _$UserProfileToJson(this);
}
7. Сравнение подходов
| Метод | Плюсы | Минусы |
|---|---|---|
| Manual fromJson | Контроль, простота | Много кода, подвержено ошибкам |
| json_serializable | Автоматизация, типобезопасность | Build step, зависимость |
| Freezed | Immutability, copyWith | Ещё одна зависимость |
8. Performance советы
// ❌ Плохо — парсит в основном потоке
final users = jsonDecode(largeJson) as List;
// ✅ Хорошо — парсит в Isolate
final users = await compute(
(jsonStr) => (jsonDecode(jsonStr) as List)
.map((item) => User.fromJson(item))
.toList(),
largeJson,
);
Рекомендация для production
Используй json_serializable для всех моделей данных. Это стандарт в Flutter сообществе:
- Меньше ошибок
- Автоматическая генерация
- Поддержка nested объектов
- Легко добавить кастомную логику
- Better type safety