在Swift 4

时间:2017-09-23 17:50:13

标签: swift ios11 json-deserialization swift4

我试图弄清楚为什么使用可选网址会导致我的JSON解码失败,使用Swift 4.我已经倾注了WWDC"什么是基金会新的"视频,苹果游乐场的例子,以及互联网上的一些地方,但还没有找到解决方案。

这是我为展示问题而创建的测试。说这是我的json数据:

let json = """
{
    "kind" : "",
    "total" : 2,
    "image_url" : "https://www.myawesomeimageURL.com/awesomeImage.jpg"
}
""".data(using: .utf8)!

这是我的结构:

    struct BusinessService: Decodable {
    let kind: String?
    let total: Int
    let image_url : URL?
}

在这里我将它序列化:

let myBusiness = try? JSONDecoder().decode(BusinessService.self, from: json)
print(myBusiness)

现在,问题是如果我收到缺少image_url的json数据,json解码失败并且给我nil。

我很困惑为什么" kind"可以是一个可选的字符串,并且当它没有值时不会导致json解码失败,但是image_url不能是一个可选的URL而不会导致json失败。

我现在已经试图解决这个问题两天了,但没有运气。有没有人知道为什么会失败?

我确实注意到了另外一个难题,那就是json中的image_url实际上并不一定是有效的图像URL。我可以输入任何字符串,我的结构被序列化,所以我想我可能误解了它是如何工作的。我认为只有当URL可以转换为有效的URL并且不会发生时,它才会自动填充。

对此的任何见解将不胜感激。

2 个答案:

答案 0 :(得分:3)

这对我来说符合预期。将image_url设置为null或将其完全遗漏会导致有效BusinessService使用nil字段的image_url值进行解码:

struct Example: Decodable {
    let url: URL?
}

// URL is present. This works, obviously.
try JSONDecoder().decode(Example.self, from: """
    { "url": "foo" }
    """.data(using: .utf8)!)

// URL is missing, indicated by null. Also works.
try JSONDecoder().decode(Example.self, from: """
    { "url": null }
    """.data(using: .utf8)!)

// URL is missing, not even the key is present. Also works.
try JSONDecoder().decode(Example.self, from: """
    {}
    """.data(using: .utf8)!) // works, too

当我提供无效的URL值(例如字段的空字符串)时,会出现唯一的问题。所以我想这是你的问题?

// This throws: "Invalid URL string."
try JSONDecoder().decode(Example.self, from: """
    { "url": "" }
    """.data(using: .utf8)!)

您必须通过发送null或将整行留空来为URL字段发送“none”值,而不是发送空字符串。

  

我很困惑为什么“kind”可以是一个可选的字符串,并且当它没有值时不会导致json解码失败,但是image_url不能是一个可选的URL而不会导致json失败。

区别在于JSON中的""是有效的String值,但不是有效的网址。

答案 1 :(得分:0)

我们可以实施一个简单的调整来处理这种情况。您可以使用 try catch 块来解码 url 项目。如果存在Invalid URL Case,那么我们可以在 try catch 块中分配空值。

    struct Example: Codable {
    let url: URL?
    
    private enum CodingKeys: String, CodingKey {
      case url
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            url = try container.decode(URL.self, forKey: .url)
        }catch( _) {
            url = nil
        }
    }
}

注意:在 swift playground 中,上面的代码仍然会导致崩溃,但它在常规 Xcode 项目中按预期工作