Кодирование данных Data из URLSession для создания объектов из JSON на Swift

Вопросы и ответы

В Swift я пытаюсь загрузить данные JSON из URL, затем использовать их для создания и заполнения объекта Swift.

Я не знаю кодировку символов возвращаемого JSON.

Я видел несколько примеров использования JSONDecoder#decode<T>(_ type: T.Type, from data: Data).

Как вышеуказанная функция знает, какую кодировку символов использовать?

Data, которую я видел, используется как аргумент для completionHandler в следующей функции из URLSession:

func dataTask(
    with request: URLRequest,
    completionHandler: @escaping @Sendable (Data?, URLResponse?, (any Error)?) -> Void
) -> URLSessionDataTask

Я не вижу никакой информации о кодировке символов в классе Data, но я вижу это в URLResponse#textEncodingName.

Знает ли Data каким-либо образом, какая у него кодировка символов? Или JSONDecoder#decode<T>(…) пытается автоматически определить кодировку символов? Или?

Если Data не содержит информации о кодировке символов, как я могу использовать её из URLResponse#textEncodingName в какой-либо функции для создания и заполнения нового объекта?

Также, если входные данные не являются корректным JSON, я хочу вывести не-JСON текст. Судя по тому, что я видел, мне нужно использовать String(data:encoding:) для преобразования из Data в String?. В данный момент у меня нет URLResponse, где мне нужно вызывать это (у меня есть только Data) из-за существующего кода от кого-то другого.

Если Data имеет кодировку символов, есть ли функция, которую я могу вызвать, которая просто принимает Data без необходимости указывать аргумент String.Encoding? Или есть ли какой-то способ прочитать кодировку символов из Data, чтобы я мог использовать её для предоставления аргумента String.Encoding?

Если я могу избежать прямого использования URLResponse, это поможет мне избежать необходимости рефакторить всё, чтобы сделать URLResponse доступным в определённых местах.

Ответ

В Swift при работе с загрузкой JSON-данных по URL и созданием объектов из этого JSON с помощью JSONDecoder, важно учитывать, что Data не содержит информации о кодировке символов, такой как UTF-8 или ISO-8859-1. Вместо этого информация о кодировке обычно содержится в заголовках ответа HTTP, доступных через объект URLResponse.

Как JSONDecoder работает с Data?

Метод JSONDecoder#decode<T>(_ type: T.Type, from data: Data) использует Data, чтобы попытаться декодировать JSON без явного указания кодировки. В большинстве случаев JSON-данные кодируются в UTF-8, и JSONDecoder предполагает это. Однако если сервер использует другую кодировку, JSON может быть некорректно декодирован, что вызывает ошибки.

Как получить кодировку текста?

Если у вас есть доступ к объекту URLResponse, вы можете узнать кодировку, используя свойство textEncodingName:

if let response = response as? HTTPURLResponse {
    let encodingName = response.textEncodingName ?? "UTF-8"
    let encoding = String.Encoding(encodingName: encodingName) ?? .utf8
}

Затем вы можете использовать эту кодировку для преобразования Data в строку:

if let string = String(data: data, encoding: encoding) {
    // Используйте строку
} else {
    // Обработка ошибки
}

Альтернатива при отсутствии доступа к URLResponse

Если у вас нет доступа к URLResponse, то, к сожалению, вы не сможете узнать кодировку напрямую. Но вы можете использовать некоторые предполагаемые методы. Например, если вы знаете, что данные, скорее всего, закодированы в UTF-8, вы можете использовать такое предположение:

let jsonString = String(data: data, encoding: .utf8) ?? String(data: data, encoding: .isoLatin1) // Пробуйте альтернативные кодировки, если нужно

Обработка некорректного JSON

Если данные не могут быть декодированы как JSON, вы можете попробовать просто преобразовать их в строку, как описано выше. Если преобразование прошло успешно, вы можете вернуть строку, чтобы проанализировать ее, если это необходимо.

Пример

Вот общий пример, который иллюстрирует загрузку JSON данных и декодирование в Swift:

let url = URL(string: "https://example.com/data.json")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
    guard let data = data, error == nil else {
        print("Ошибка: \(error?.localizedDescription ?? "Неизвестная ошибка")")
        return
    }

    // Если response доступен, используйте его для определения кодировки
    if let httpResponse = response as? HTTPURLResponse,
       let encodingName = httpResponse.textEncodingName,
       let encoding = String.Encoding(encodingName: encodingName) {

        if let jsonString = String(data: data, encoding: encoding) {
            print("Полученная строка: \(jsonString)")

            // Попытка декодировать JSON
            let decoder = JSONDecoder()
            do {
                let myObject = try decoder.decode(MyObject.self, from: data)
                // Используйте объект
            } catch {
                print("Не удалось декодировать JSON: \(error.localizedDescription)")
                // Обработка случая некорректного JSON
            }
        }
    } else {
        // Если response не доступен, используем UTF-8 по умолчанию
        let jsonString = String(data: data, encoding: .utf8) ?? "Не удалось преобразовать Data в строку."
        print("Полученная строка: \(jsonString)")
    }
}
task.resume()

В этом примере обрабатываются как успех загрузки и декодирования, так и ошибки кодирования, при этом если нет доступа к URLResponse, предполагается кодировка по умолчанию (UTF-8).

Оцените материал
Добавить комментарий

Капча загружается...