Если вы когда-либо работали с данными, отправляли что-то на сервер или получали ответ от API, то наверняка сталкивались с
jsonEncode и jsonDecode. Это методы из библиотеки dart:convert, которые отвечают за кодирование и декодирование данных. Давайте рассмотрим подробнее!Библиотека dart:convert содержит конвертеры для JSON и UTF-8, а также инструменты для создания собственных преобразований.
Поскольку серверы не умеют работать с объектами Dart напрямую, данные перед отправкой нужно превратить в строку (обычно в JSON). Для этого используют jsonEncode, а чтобы получить структуру данных обратно из строки — jsonDecode.
Например:
import 'dart:convert';
void main() {
final data = {'name': 'Helen', 'age': 22};
final encoded = jsonEncode(data);
print(encoded); // {"name":"Helen","age":22}
final decoded = jsonDecode(encoded);
print(decoded['name']); // Helen
}
Нужно помнить, что jsonDecode возвращает значение типа
dynamic: ◾️для объекта это обычно
Map<String, dynamic>, ◾️для массива —
List<dynamic>. Поэтому после декодирования полезно явно приводить типы, чтобы избежать ошибок:
final name = decoded['name'] as String;
JSON поддерживает только простые типы: int, double, String, bool, null, List, Map<String, dynamic>. Когда в модели появляются вложенные объекты или кастомные типы, простая сериализация перестает работать.
class User {
final String name;
final int age;
final Address address;
User(this.name, this.age, this.address);
}Когда jsonEncode обрабатывает объект User, он ищет стандартный способ преобразования каждого поля. Для String и int он его находит. Но для поля address типа Address Dart не имеет встроенного правила. В отсутствие метода toJson(), он выбрасывает
UnsupportedError.В таких ситуациях нужно вручную реализовать toJson()/fromJson() (рекурсивно, если структура вложенная):
Map<String, dynamic> toJson() => {
'name': name,
'age': age,
'address': address.toJson(),
};
// и аналогично для fromJson.То же самое касается и нестандартных типов: DateTime, enum, Uri и других. JSON их не понимает, поэтому вам нужно вручную преобразовывать их в строку:
'createdAt': createdAt.toIso8601String(),
'status': status.name, // enum Status {online, offline}
'url': url.toString(),
Но ручное преобразование может быть рутинным и долгим, особенно если у вас огромный пакет, где 50+ моделей и сложные вложенные структуры.
Для этого в Dart есть библиотеки. Наиболее популярные:
✔️json_serializable — генерирует
toJson() и fromJson() ✔️ freezed — генерирует иммутабельные модели, копирование, сравнение и также JSON-сериализацию
Пример с freezed выглядит так:
@freezed
class User with _$User {
const factory User({
required String name,
required int age,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
После генерации сериализация сводится к паре строк:
final json = user.toJson();
final user2 = User.fromJson(json);
Но перед использованием библиотек советую прочитать их документацию, так как у них есть свои нюансы.
❤️ — если было полезно
