【问题标题】:Strings fetched with JSON-API to be converted from base64 to UTF8 in Swift使用 JSON-API 获取的字符串将在 Swift 中从 base64 转换为 UTF8
【发布时间】:2019-04-14 01:36:08
【问题描述】:

我正在开发一个从Open Trivia Database (API) 获取琐事问题的 iOS 应用程序 在阅读文档并使用它之后,我认为最好的解决方案是使用 base64 编码(因为它似乎在 Swift 中受支持)。我已成功获取数据并使用 JSONParser 将其解析为结构。我必须解决的问题是如何将值从 base64 转换为 UTF8。 (键被正确读取,因此它映射到我的结构)

我的第一个想法是使用decoder.dataDecodingStrategy = .base64,但这似乎根本没有任何效果。我不太确定为什么。

这是正确的方法吗,还是在将字符串读入结构后我应该自己解码? 简而言之,解析的结果是一个结构,其中包含一个作为 Int 的 responseCode 和一个数组,其中包含表示问题的结构,我想将其转换为 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()

【问题讨论】:

    标签: json swift utf-8 character-encoding base64


    【解决方案1】:

    Base64 编码用于您声明为数据而不是字符串的属性,如下所示:

    struct Response: Codable {
    
       let someBaseEncodedString: Data
    
    
       var someString: String? {
          get {
             return String(data: someBaseEncodedString, encoding: .utf8)
          }
       }
    }
    

    因此,对于您给出的示例,作为 base64 编码字符串返回的所有属性都应该在您的结构中具有 Data 类型,然后解码为字符串。

    【讨论】:

    • 这对我帮助很大,我不必更改很多代码。谢谢!
    【解决方案2】:

    根据其他答案的建议,您可以在 JSONSerializationJSONDecoder 解码 API 结果后解码 Data 或 Base-64 String

    但是如果你更喜欢写解码初始化器,你可以这样写:

    我猜这可能和你自己的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 似乎支持它),但这是真的吗?

    JSONDecoder 支持 Base-64 到 Data,但这不是您所期望的。因此,使用另一种编码可能是更好的选择。

    但是,无论如何,JSON 字符串可以仅使用带有 \uXXXX\uHHHH\uLLLL 的 ASCII 表示所有 unicode 字符。所以,我不明白为什么 API 设计者不提供选项标准 JSON 编码。如果您可以联系他们,请告诉他们提供选项,这可能会简化许多客户端代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-26
      • 2014-05-20
      • 1970-01-01
      • 2017-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多