Работа с JSON в Swift: парсинг и сериализация
JSON (JavaScript Object Notation) стал стандартом де-факто для обмена данными между клиентом и сервером в мобильных и десктопных приложениях. В экосистеме Swift (iOS/macOS) работа с JSON реализована на высшем уровне — от простого JSONSerialization до мощного протокола Codable. В этой статье мы разберём все основные способы парсинга и сериализации JSON в Swift, приведём практические примеры и покажем, как избежать типичных ошибок.
1. Основы работы с JSON в Swift: JSONSerialization
До появления Swift 4 основным инструментом для работы с JSON был класс JSONSerialization, входящий в Foundation. Он позволяет преобразовывать данные JSON в объекты Swift (словари, массивы, строки, числа) и обратно. Этот метод остаётся актуальным, если вам нужен полный контроль над процессом или вы работаете с устаревшим кодом.
Парсинг JSON с помощью JSONSerialization
Предположим, мы получили JSON-строку с сервера:
let jsonString = """
{
"name": "Иван",
"age": 30,
"isPremium": true,
"hobbies": ["чтение", "бег", "программирование"]
}
"""
guard let jsonData = jsonString.data(using: .utf8) else {
fatalError("Ошибка кодирования строки в Data")
}
do {
if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
let name = jsonObject["name"] as? String ?? ""
let age = jsonObject["age"] as? Int ?? 0
let isPremium = jsonObject["isPremium"] as? Bool ?? false
let hobbies = jsonObject["hobbies"] as? [String] ?? []
print("Имя: \(name), Возраст: \(age), Премиум: \(isPremium), Хобби: \(hobbies)")
}
} catch {
print("Ошибка парсинга JSON: \(error.localizedDescription)")
}
Недостаток этого подхода — необходимость вручную приводить типы и обрабатывать опциональные значения. Код становится громоздким и подвержен ошибкам.
Сериализация в JSON с помощью JSONSerialization
Обратный процесс — превращение словаря Swift в JSON-данные:
let personDict: [String: Any] = [
"name": "Мария",
"age": 25,
"isPremium": false,
"hobbies": ["рисование", "йога"]
]
do {
let jsonData = try JSONSerialization.data(withJSONObject: personDict, options: .prettyPrinted)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
} catch {
print("Ошибка сериализации: \(error.localizedDescription)")
}
Опция .prettyPrinted делает вывод читаемым для человека. Для продакшена обычно используют [] (без форматирования) для экономии трафика.
2. Современный подход: протокол Codable и JSONDecoder
Начиная с Swift 4, работа с JSON стала значительно проще благодаря протоколу Codable (который объединяет Encodable и Decodable). Вы просто описываете структуру данных, и компилятор автоматически генерирует код для кодирования/декодирования.
Парсинг JSON с помощью JSONDecoder
Определим модель для нашего JSON:
struct Person: Codable {
let name: String
let age: Int
let isPremium: Bool
let hobbies: [String]
}
let jsonString = """
{
"name": "Иван",
"age": 30,
"isPremium": true,
"hobbies": ["чтение", "бег", "программирование"]
}
"""
guard let jsonData = jsonString.data(using: .utf8) else {
fatalError("Ошибка кодирования строки")
}
do {
let person = try JSONDecoder().decode(Person.self, from: jsonData)
print("Имя: \(person.name), Возраст: \(person.age), Премиум: \(person.isPremium), Хобби: \(person.hobbies)")
} catch {
print("Ошибка декодирования: \(error.localizedDescription)")
}
Как видите, код стал намного чище. Всё приведение типов и проверки выполняются автоматически. Если JSON не соответствует структуре, вы получите понятную ошибку.
Сериализация в JSON с помощью JSONEncoder
Превратить объект обратно в JSON так же просто:
let person = Person(name: "Мария", age: 25, isPremium: false, hobbies: ["рисование", "йога"])
do {
let jsonData = try JSONEncoder().encode(person)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
} catch {
print("Ошибка кодирования: \(error.localizedDescription)")
}
По умолчанию JSONEncoder генерирует компактный JSON без лишних пробелов. Для отладки можно использовать опцию .outputFormatting = .prettyPrinted.
3. Продвинутые техники: кастомные ключи, вложенные объекты и обработка ошибок
В реальных проектах JSON часто приходит с ключами в snake_case, а в Swift принято использовать camelCase. Или же вам нужно пропустить некоторые поля. Codable легко справляется с этими задачами.
Работа с ключами в snake_case
Допустим, сервер возвращает JSON с ключами full_name и is_premium. В Swift мы хотим использовать fullName и isPremium. Для этого нужно определить перечисление CodingKeys:
struct Person: Codable {
let fullName: String
let age: Int
let isPremium: Bool
let hobbies: [String]
enum CodingKeys: String, CodingKey {
case fullName = "full_name"
case age
case isPremium = "is_premium"
case hobbies
}
}
let jsonString = """
{
"full_name": "Иван Петров",
"age": 30,
"is_premium": true,
"hobbies": ["чтение", "бег"]
}
"""
// Декодирование работает аналогично предыдущему примеру
Вложенные JSON-объекты
Часто данные имеют вложенную структуру. Codable поддерживает это из коробки:
struct Address: Codable {
let city: String
let street: String
}
struct User: Codable {
let name: String
let address: Address
}
let jsonString = """
{
"name": "Анна",
"address": {
"city": "Москва",
"street": "Тверская"
}
}
"""
let json