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

Как десериализуешь JSON?

1.3 Junior🔥 231 комментариев
#Dart#Работа с сетью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Десериализация 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 — основа надёжного мобильного приложения.