【问题标题】:Decode URLs with special characters in Swift在 Swift 中使用特殊字符解码 URL
【发布时间】:2020-09-09 00:35:04
【问题描述】:

我使用的一个 API 提供了 URL 链接,其中可以包含特殊字符,例如“http://es.dbpedia.org/resource/Analisis_de_datos”(里面的字母“á”)。

这是一个绝对有效的 URL,但是,如果可解码类包含可选的 URL? 变量,则无法对其进行解码。

我可以在我的班级中将URL? 更改为String?,并有一个类似于URL(string: urlString.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) 的计算变量,但也许有更优雅的解决方案。

在 Playground 中重现:

struct Container: Encodable {
    let url: String
}

struct Response: Decodable {
    let url: URL?
}

let container = Container(url: "http://es.dbpedia.org/resource/Análisis_de_datos")

let encoder = JSONEncoder()
let encodedData = try encoder.encode(container)
    
let decoder = JSONDecoder()
let response = try? decoder.decode(Response.self, from: encodedData)
// response == nil, as it can't be decoded.

let url = response?.url 

【问题讨论】:

  • 自定义解码怎么样?你考虑过吗?
  • 是的,我有。但是,该类本身有大约 20 个变量和 12 个嵌套结构。这不值得)

标签: json swift url codable decodable


【解决方案1】:

有多种方法可以克服这个问题,但我认为使用属性包装器可能是最优雅的:

@propertyWrapper
struct URLPercentEncoding {
   var wrappedValue: URL
}

extension URLPercentEncoding: Decodable {
   public init(from decoder: Decoder) throws {
      let container = try decoder.singleValueContainer()
        
      if let str = try? container.decode(String.self),
         let encoded = str.addingPercentEncoding(
                              withAllowedCharacters: .urlFragmentAllowed),
         let url = URL(string: encoded) {

         self.wrappedValue = url

      } else {
         throw DecodingError.dataCorrupted(
            .init(codingPath: container.codingPath, debugDescription: "Corrupted url"))
      }
   }
}

然后您可以像这样使用它,而无需该模型的消费者对此有所了解:

struct Response: Decodable {
    @URLPercentEncoding let url: URL
}

【讨论】:

  • 谢谢,真的很好,
【解决方案2】:

您可以扩展 KeyedDecodingContainer 并实现自己的 URL 解码方法:

extension KeyedDecodingContainer {
    func decode(_ type: URL.Type, forKey key: K) throws -> URL {
        let string = try decode(String.self, forKey: key)
        guard let url = URL(string: string.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "")
        else {
            throw DecodingError.dataCorrupted(.init(codingPath: codingPath, debugDescription: "The stringvalue for the key \(key) couldn't be converted into a URL value: \(string)"))
        }
        return url
    }
    // decoding an optional URL
    func decodeIfPresent(_ type: URL.Type, forKey key: K) throws -> URL? {
        try URL(string: decode(String.self, forKey: key).addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "")
    }
}

struct Container: Encodable {
    let url: String
}

struct Response: Decodable {
    let url: URL
}

let container = Container(url: "http://es.dbpedia.org/resource/Análisis_de_datos")

do {
    let encodedData = try encoder.encode(container)
    print(String(data: encodedData, encoding: .utf8))
    let decoder = JSONDecoder()
    let response = try decoder.decode(Response.self, from: encodedData)
    print(response)
} catch {
    print(error)
}

这将打印:

可选("{"url":"http:\/\/es.dbpedia.org\/resource\/Analisis_de_datos"}")

响应(网址:http://es.dbpedia.org/resource/An%C3%A1lisis_de_datos

【讨论】:

  • 非常感谢!
猜你喜欢
  • 2020-05-01
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
  • 1970-01-01
  • 2013-06-06
  • 1970-01-01
  • 2021-07-16
  • 2014-09-17
相关资源
最近更新 更多