I have the following structure and response parsing fails because url (https://example.url.com/test a
) has a space in it. How can I escape it with %20 value in deserialization layer and keep it as URL type?
struct Response: Decodable {
let url: URL
enum CodingKeys: String, CodingKey {
case url
}
init(from decoder: Decoder) throws {
self.url = try decoder.container(keyedBy: CodingKeys.self).decode(URL.self, forKey: .url)
}
}
let string = "{\"url\":\"https://example.url.com/test a\"}"
let responseModel = try? JSONDecoder().decode(Response.self, from: string.data(using: .utf8)!)
print(responseModel?.url)
As Rob has already mentioned it would be better to fix the issue at the backend instead of fixing the decoding part. If you can not fix it at the server side you can decote your url as a string, percent escape it and then use the resulting string to initialize your url property:
struct Response: Codable {
let url: URL
}
extension Response {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let string = try container.decode(String.self, forKey: .url)
guard
let percentEncoded = string
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let url = URL(string: percentEncoded)
else {
throw DecodingError.dataCorruptedError(forKey: .url,
in: container,
debugDescription: "Invalid url string: \(string)")
}
self.url = url
}
}
Playground testing
let jsonString = #"{"url":"https://example.url.com/test a"}"#
do {
let responseModel = try JSONDecoder().decode(Response.self, from: Data(jsonString.utf8))
print(responseModel.url)
} catch { print(error) }
This will print