← Назад к вопросам

Как работать с 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, зависимость
FreezedImmutability, 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 сообществе:

  1. Меньше ошибок
  2. Автоматическая генерация
  3. Поддержка nested объектов
  4. Легко добавить кастомную логику
  5. Better type safety
Как работать с JSON и сериализацией данных во Flutter? | PrepBro