Строки, извлеченные с помощью JSON-API для преобразования из base64 в UTF8 в Swift - PullRequest
0 голосов
/ 11 ноября 2018

Я разрабатываю приложение для iOS, которое извлекает вопросы викторины из Открыть базу данных викторин (API) Прочитав документы и поигравшись с ними, я думаю, что лучшее решение - это использовать кодирование base64 (так как оно, похоже, поддерживается в Swift). Я успешно извлек данные и проанализировал их с помощью JSONParser. Проблема, которую я должен решить, состоит в том, как преобразовать значения из base64 в UTF8. (Ключи прочитаны правильно, и поэтому он соответствует моим структурам)

Моя первая идея состояла в том, чтобы использовать decoder.dataDecodingStrategy = .base64, но это, похоже, не имеет никакого эффекта вообще. И я не совсем уверен, почему.

Это правильный способ сделать это, или я должен сам потом декодировать его, когда строки считываются в структуры? Короче говоря, результатом синтаксического анализа является структура, содержащая responseCode в виде Int и массив, содержащий структуры, представляющие вопросы со строками, которые я хочу преобразовать в UTF8 в качестве членов

Мой код для разбора выглядит так:

  let urlPath = "https://opentdb.com/api.php?amount=10&encode=base64"
    let apiURL = URL(string: urlPath)!

    URLSession.shared.dataTask(with: apiURL) { (data, response, error) in
        guard let data = data else {return}
        do{
            let decoder = JSONDecoder()
            decoder.dataDecodingStrategy = .base64
            let questionData = try decoder.decode(Response.self, from: data)
            print(questionData)
        }catch let err{
            print("Error", err)
        }
        }.resume()

Ответы [ 2 ]

0 голосов
/ 11 ноября 2018

Как следует из других ответов, вы можете декодировать Data или Base-64 String после того, как JSONSerialization или JSONDecoder декодировали результаты API.

Но если вы предпочитаете писать инициализатор декодирования, вы можете сделать это следующим образом:

Возможно, это не сильно отличается от вашего Response.

struct Response: Codable {
    var responseCode: Int
    var results: [Result]

    enum CodingKeys: String, CodingKey {
        case responseCode = "response_code"
        case results
    }
}

Чтобы подготовиться к написанию инициализатора декодирования для Response, я хотел бы использовать некоторые расширения:

extension KeyedDecodingContainer {
    func decodeBase64(forKey key: Key, encoding: String.Encoding) throws -> String {
        guard let string = try self.decode(String.self, forKey: key).decodeBase64(encoding: encoding) else {
            throw DecodingError.dataCorruptedError(forKey: key, in: self,
                                                   debugDescription: "Not a valid Base-64 representing UTF-8")
        }
        return string
    }

    func decodeBase64(forKey key: Key, encoding: String.Encoding) throws -> [String] {
        var arrContainer = try self.nestedUnkeyedContainer(forKey: key)
        var strings: [String] = []
        while !arrContainer.isAtEnd {
            guard let string = try arrContainer.decode(String.self).decodeBase64(encoding: encoding) else {
                throw DecodingError.dataCorruptedError(forKey: key, in: self,
                                                       debugDescription: "Not a valid Base-64 representing UTF-8")
            }
            strings.append(string)
        }
        return strings
    }
}

Используя эти расширения выше, вы можете определить тип Result следующим образом:

extension Response {
    struct Result: Codable {
        var category: String
        var type: String
        var difficulty: String
        var question: String
        var correctAnswer: String
        var incorrectAnswers: [String]

        enum CodingKeys: String, CodingKey {
            case category
            case type
            case difficulty
            case question
            case correctAnswer = "correct_answer"
            case incorrectAnswers = "incorrect_answers"
        }

        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.category = try container.decodeBase64(forKey: .category, encoding: .utf8)
            self.type = try container.decodeBase64(forKey: .type, encoding: .utf8)
            self.difficulty = try container.decodeBase64(forKey: .difficulty, encoding: .utf8)
            self.question = try container.decodeBase64(forKey: .question, encoding: .utf8)
            self.correctAnswer = try container.decodeBase64(forKey: .correctAnswer, encoding: .utf8)
            self.incorrectAnswers = try container.decodeBase64(forKey: .incorrectAnswers, encoding: .utf8)
        }
    }
}

(Вы не упомянули, определен ли ваш Response (или другое имя?) Как вложенный тип или нет, но я думаю, что вы можете переименовать или изменить его самостоятельно.)

Учитывая все вышесказанное, вы можете просто декодировать ответ API как:

        do {
            let decoder = JSONDecoder()
            let questionData = try decoder.decode(Response.self, from: data)
            print(questionData)
        } catch {
            print("Error", error)
        }

Кстати, вы говорите Я думаю, что лучшее решение - это использовать кодирование base64 (так как оно поддерживается в Swift) , но так ли это на самом деле?

Base-64 до Data поддерживается в JSONDecoder, но это не то, что вы ожидаете. Таким образом, использование другой кодировки может быть лучшим выбором.

Но, в любом случае, строка JSON может представлять все символы Юникода, используя только ASCII с \uXXXX или \uHHHH\uLLLL. Поэтому я не понимаю, почему разработчики API не предоставляют опцию Стандартная кодировка JSON . Если вы можете связаться с ними, скажите им, чтобы они предоставили опцию, которая может упростить многие клиентские коды.

0 голосов
/ 11 ноября 2018

Кодировка Base64 используется для свойств, которые вы объявили как Data, а не как Strings, например:

struct Response: Codable {

   let someBaseEncodedString: Data


   var someString: String? {
      get {
         return String(data: someBaseEncodedString, encoding: .utf8)
      }
   }
}

Таким образом, для приведенного вами примера все свойства, возвращаемые в виде строки в кодировке base64, должны иметь тип данных в вашей структуре, а затем декодироваться как строки.

...