Swift 4 JSON解码器

时间:2017-10-26 23:56:58

标签: ios json swift swift4 decodable

我知道其他问题已经涵盖了这个问题,但是我已经跟着他们了,我仍然感到难过。这是我的JSON结构:

      {
       "FindBoatResult": {
       "num_boats": 10,
       "boat": [
         {
           "num_segments": 1,
           "segments": [
              {
               "ident": "String",
                "origin" : {
                         "code" : "String"
                 },
           },
         ]
        }
等等......但那就像结构一样深。 "段"有多个回报。在每个JSON响应中。在Swift中我有这个代码。

struct Result : Decodable {
    let FindBoatResult : FindBoatResult
}
struct FindBoatResult : Decodable {
    let boats : Boats
    let num_boats : Int
}
struct Boats : Decodable {
    let segments : [Segments]
}
struct Segments : Decodable {
    let ident : String?
    let origin : Origin
}
struct Origin : Decodable {
    let code : String
}

func getBoats() {

let urlString = "http://myApi"
guard let url = URL(string: urlString) else { return }

    URLSession.shared.dataTask(with: url) { (data, response, err) in
        guard let data = data else {return}
        let dataAsString = String(data: data, encoding: .utf8)
        //print(dataAsString)

        do {
            let boats = try
                JSONDecoder().decode(FindBoatResult.self, from: data)

            print(boats)
        } catch {
            print(err) 
        }  
    }.resume()
}

这失败并抛出错误,但错误的打印为零。所以我无法告诉我错过了什么。 dataAsString按预期打印出JSON,所以我知道"数据"很好。

2 个答案:

答案 0 :(得分:1)

我发现了一些小问题。尝试替换它:

struct FindBoatResult: Decodable {
    let boats: Boats
    let num_boats: Int
}
struct Boats: Decodable {
    let segments: [Segments]
}

使用:

struct FindBoatResult: Decodable {
    let boat: [Boat]
    let num_boats: Int
}
struct Boat: Decodable {
    let segments: [Segments]
}

最后,使用Result类型(不是FindBoatResult)进行解码:

JSONDecoder().decode(Result.self, from: data)

答案 1 :(得分:1)

扩展了Paulo的回答,我可能会进一步建议,如果您遇到JSON,其密钥不符合属性名称的Swift约定,那么您使用{{1模式将JSON键转换为更好的Swift属性名称,例如:

CodingKeys

因此,例如,当您表示一个对象数组时,使用复数(例如struct BoatResult: Decodable { // I'd simplify this name let boatCollection: BoatCollection enum CodingKeys: String, CodingKey { case boatCollection = "FindBoatResult" } } struct BoatCollection: Decodable { // I'd simplify this, too, removing "Find" from the name; verbs are for methods, not properties let boats: [Boat] let numberOfBoats: Int enum CodingKeys: String, CodingKey { case boats = "boat" // "boat" isn't great property name for an array of boats, so let's map the poor JSON key to better Swift name here case numberOfBoats = "num_boats" // likewise, let's map the "_" name with better camelCase property name } } struct Boat: Decodable { // This entity represents a single boat, so let's use "Boat", not "Boats" let segments: [Segment] } struct Segment: Decodable { // This entity represents a single segment, so let's use "Segment", not "Segments" let identifier: String let origin: Origin enum CodingKeys: String, CodingKey { case identifier = "ident" // `ident` isn't a common name for identifier, so let's use something more logical case origin } } struct Origin: Decodable { let code: String } ),并使用boats将误导性的CodingKeys JSON键映射到这个名为boat的数组引用更好。或者当你有一个像boats这样的密钥时,不要觉得你必须在你的Swift属性中使用那个坏名字并使用更好的东西,如num_boats(或numberOfBoats或其他),并且失去了count语法,这非常不合情理。

显然,如果你掌握了JSON的设计,你可以在那里修复一些选择不当的密钥名称,但即使你决定要让你的web服务使用{{1}语法,继续并使用_来确保你的Swift对象符合camelCase约定。