Преобразование словаря в JSON в Swift: пошаговое руководство
Основная проблема и её решение
Ваша ошибка возникает из-за того, что JSONSerialization.data(withJSONObject:options:) требует, чтобы все значения в словаре были JSON-совместимыми. В Swift числовые типы (Int, Double), строки, булевы значения и массивы/словари с такими же типами — всё ок. Но если вы случайно передали nil или нестандартный тип (например, Date), то получите ошибку.
В вашем коде всё выглядит корректно, но давайте разберём правильный подход шаг за шагом.
Шаг 1: Базовое преобразование с обработкой ошибок
import Foundation
let userDict: [String: Any] = [
"name": "Иван",
"age": 30,
"isActive": true,
"address": [
"city": "Москва",
"zip": "101000"
]
]
do {
let jsonData = try JSONSerialization.data(withJSONObject: userDict, options: .prettyPrinted)
// Теперь jsonData можно отправить на сервер или сохранить
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
} catch {
print("Ошибка сериализации: \(error.localizedDescription)")
}
Этот код выведет отформатированный JSON:
{
"name" : "Иван",
"age" : 30,
"isActive" : true,
"address" : {
"city" : "Москва",
"zip" : "101000"
}
}
Шаг 2: Обратное преобразование (JSON → словарь)
let jsonString = """
{
"name" : "Иван",
"age" : 30,
"isActive" : true
}
"""
if let jsonData = jsonString.data(using: .utf8) {
do {
if let dict = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
print(dict["name"] as? String ?? "")
}
} catch {
print("Ошибка десериализации: \(error)")
}
}
Важные моменты
- Типы данных: Убедитесь, что все значения —
String,Int,Double,Bool,[Any],[String: Any]илиnil(который станетnull). Если естьDate, преобразуйте его в строку черезISO8601DateFormatter. - Опции форматирования:
.prettyPrintedдобавляет отступы для читаемости,.sortedKeysсортирует ключи (полезно для тестирования). - Безопасность: Всегда оборачивайте вызов в
do-catch— сериализация может выбросить исключение. - Альтернатива с Codable: Если у вас фиксированная структура данных, лучше использовать протокол
Codable— это безопаснее и удобнее.
Пример с Codable (рекомендуемый подход для сложных данных)
struct User: Codable {
let name: String
let age: Int
let isActive: Bool
let address: Address
}
struct Address: Codable {
let city: String
let zip: String
}
let user = User(name: "Иван", age: 30, isActive: true,
address: Address(city: "Москва", zip: "101000"))
do {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(user)
print(String(data: jsonData, encoding: .utf8)!)
} catch {
print("Ошибка: \(error)")
}
Так вы избегаете проблем с типами и получаете строгую типизацию на уровне компилятора.
<<Упрощённый подход с JSONSerialization и проверкой типов
Если вы не хотите использовать Codable (например, для динамических данных), можно написать вспомогательную функцию, которая рекурсивно проверяет значения:
func safeJSONObject(from dict: [String: Any]) -> [String: Any] {
var result = [String: Any]()
for (key, value) in dict {
switch value {
case is String, is Int, is Double, is Bool, is NSNull:
result[key] = value
case let nestedDict as [String: Any]:
result[key] = safeJSONObject(from: nestedDict)
case let nestedArray as [Any]:
result[key] = nestedArray.map { element in
if let dictElement = element as? [String: Any] {
return safeJSONObject(from: dictElement)
}
return element
}
default:
// Если тип не поддерживается, преобразуем в строку
result[key] = "\(value)"
}
}
return result
}
// Использование
let cleanDict = safeJSONObject(from: userDict)
let jsonData = try JSONSerialization.data(withJSONObject: cleanDict, options: .prettyPrinted)
Этот код автоматически заменяет неподдерживаемые типы (например, Date) на их строковое представление. Однако помните: для production-кода лучше использовать Codable или явно преобразовывать каждый тип. Данный метод — быстрый костыль для прототипов.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги