Avatar ·

Как преобразовать строку JSON в словарь (Dictionary) в Swift?

Всем привет! Я новичок в Swift и столкнулся с проблемой при работе с JSON. Получаю от сервера строку в формате JSON, например: `{"name":"John","age":30,"city":"New York"}`. Мне нужно преобразовать эту строку в словарь `[String: Any]`, чтобы я мог обращаться к значениям по ключам. Я пробовал использовать `JSONSerialization`, но у меня ничего не получается. Вот мой код: ```swift let jsonString = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}" if let jsonData = jsonString.data(using: .utf8) { do { let dict = try JSONSerialization.jsonObject(with: jsonData, options: []) print(dict) } catch { print("Ошибка: \(error.localizedDescription)") } } ``` Этот код выводит словарь в консоль, но Xcode ругается, что `dict` имеет тип `Any`, а не `[String: Any]`. Как правильно привести результат к словарю? И что делать, если JSON содержит вложенные объекты или массивы? Буду благодарен за подробное объяснение с примерами! <<>>

Правильное преобразование JSON строки в словарь

Ваш код почти правильный, но есть одна важная деталь: метод jsonObject(with:options:) возвращает Any, потому что JSON может быть не только словарём, но и массивом. Вам нужно явно привести результат к нужному типу. Вот исправленный вариант:

import Foundation

let jsonString = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}"

if let jsonData = jsonString.data(using: .utf8) {
    do {
        if let dict = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
            print(dict) // ["name": "John", "age": 30, "city": "New York"]
            // Теперь можно обращаться по ключам
            if let name = dict["name"] as? String {
                print("Имя: \(name)") // Имя: John
            }
            if let age = dict["age"] as? Int {
                print("Возраст: \(age)") // Возраст: 30
            }
        } else {
            print("Не удалось преобразовать JSON в словарь")
        }
    } catch {
        print("Ошибка парсинга JSON: \(error.localizedDescription)")
    }
}

Почему это работает?

Ключевой момент — использование as? [String: Any]. Это опциональное приведение (optional cast). Если JSON действительно является словарём с ключами-строками, вы получите [String: Any]. Если нет (например, если JSON — это массив), то условие if let не сработает, и вы сможете обработать этот случай отдельно.

Что делать с вложенными JSON?

Если ваш JSON содержит вложенные объекты, они тоже будут иметь тип Any. Вам нужно последовательно приводить их к нужным типам:

let nestedJson = """
{
    "user": {
        "name": "Alice",
        "age": 25
    },
    "hobbies": ["reading", "swimming"]
}
"""

if let data = nestedJson.data(using: .utf8),
   let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
    // Достаём вложенный словарь
    if let user = dict["user"] as? [String: Any] {
        print(user["name"] as? String ?? "") // Alice
    }
    // Достаём массив
    if let hobbies = dict["hobbies"] as? [String] {
        print(hobbies) // ["reading", "swimming"]
    }
}

Альтернативный подход с Decodable (рекомендуется для сложных структур)

Для продакшн-кода лучше использовать протокол Codable. Это безопаснее и читаемее:

struct User: Codable {
    let name: String
    let age: Int
    let city: String
}

let jsonString = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}"
if let data = jsonString.data(using: .utf8) {
    do {
        let user = try JSONDecoder().decode(User.self, from: data)
        print(user.name) // John
        print(user.age)  // 30
    } catch {
        print("Ошибка декодирования: \(error)")
    }
}

Важные моменты

  • Всегда проверяйте, что строка JSON корректна (используйте do-catch)
  • Не забывайте про опциональное приведение типов — as? [String: Any]
  • Для сложных структур данных используйте Codable вместо ручного парсинга
  • Помните, что JSONSerialization работает синхронно — для больших данных лучше выносить в фоновый поток
<<>>

Использование JSONSerialization с опцией .mutableContainers

Если вам нужно изменять полученный словарь (например, добавлять новые ключи), можно использовать опцию .mutableContainers. Она создаёт изменяемые версии словарей и массивов:

let jsonString = "{\"name\":\"John\",\"age\":30}"
if let data = jsonString.data(using: .utf8) {
    do {
        if var dict = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
            dict["country"] = "USA" // Теперь можно изменять словарь
            print(dict) // ["name": "John", "age": 30, "country": "USA"]
        }
    } catch {
        print(error)
    }
}

Обратите внимание: .mutableContainers возвращает NSMutableDictionary и NSMutableArray, которые мостятся (bridge) к стандартным типам Swift. Это удобно, если нужно модифицировать данные после парсинга, но для большинства задач лучше использовать Codable — он безопаснее по типам и не требует постоянных приведений.

Также полезно знать, что если ваш JSON — это массив (начинается с [), то приведение нужно делать к [[String: Any]]:

let arrayJson = "[{\"id\":1},{\"id\":2}]"
if let data = arrayJson.data(using: .utf8),
   let array = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] {
    print(array.count) // 2
}