【问题标题】:Parsing nested JSON using Decodable in Swift在 Swift 中使用 Decodable 解析嵌套的 JSON
【发布时间】:2020-10-07 17:25:44
【问题描述】:

我正在尝试解析这个 JSON 响应

    {
    "payload": {
        "bgl_category": [{
            "number": "X",
            "name": "",
            "parent_number": null,
            "id": 48488,
            "description": "Baustellenunterk\u00fcnfte, Container",
            "children_count": 6
        }, {
            "number": "Y",
            "name": "",
            "parent_number": null,
            "id": 49586,
            "description": "Ger\u00e4te f\u00fcr Vermessung, Labor, B\u00fcro, Kommunikation, \u00dcberwachung, K\u00fcche",
            "children_count": 7
        }]
    },
    "meta": {
        "total": 21
    }
}

我有兴趣在 TableViewCell 中查看的只是 numberdescription

这是我尝试过的:

    //MARK: - BGLCats
struct BGLCats: Decodable {

        let meta : Meta!
        let payload : Payload!
        
}

//MARK: - Payload
struct Payload: Decodable {

        let bglCategory : [BglCategory]!
        
}

//MARK: - BglCategory
struct BglCategory: Decodable {

        let descriptionField : String
        let id : Int
        let name : String
        let number : String
        let parentNumber : Int
        
}

//MARK: - Meta
struct Meta: Decodable {

        let total : Int
        
}

API 请求:

    fileprivate func getBgls() {
        
        guard let authToken = getAuthToken() else {
            return
        }
        
        let headers  = [
            "content-type" : "application/json",
            "cache-control": "no-cache",
            "Accept"       : "application/json",
            "Authorization": "\(authToken)"
        ]
        
        let request = NSMutableURLRequest(url: NSURL(string: "https://api-dev.com")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
        
        request.allHTTPHeaderFields = headers
        
        let endpoint  = "https://api-dev.com"
        guard let url = URL(string: endpoint) else { return }
        
        URLSession.shared.dataTask(with: request as URLRequest) {(data, response, error) in
            guard let data = data else { return }
            
            do {
                let BGLList = try JSONDecoder().decode(BglCategory.self, from: data)
                print(BGLList)
                
                DispatchQueue.main.sync { [ weak self] in
                    self?.number = BGLList.number
                    self?.desc   = BGLList.descriptionField
//                    self?.id  = BGLList.id

                    print("Number: \(self?.number ?? "Unknown" )")
                    print("desc: \(self?.desc ?? "Unknown" )")
//                    print("id: \(self?.id ?? 0 )")
                }
            } catch let jsonError {
                print("Error Serializing JSON:", jsonError)
            }
            
       }.resume()
    }

但我遇到了错误:

Error Serializing JSON: keyNotFound(CodingKeys(stringValue: "childrenCount", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"childrenCount\", intValue: nil) (\"childrenCount\").", underlyingError: nil))

【问题讨论】:

  • 错误信息与 JSON 和代码不匹配。你应该得到其他错误。当您使用 camelCased 名称时,您必须至少指定 convertFromSnakeCase 策略,并且根对象是 BGLCats。并且请:如果有本地对应类,请不要使用 NS... 类。

标签: json swift decodable


【解决方案1】:

这里有几个问题。

您(大部分)正确地创建了模型,但只有两个不匹配:

struct BglCategory: Decodable {

   let description : String // renamed, to match "description" in JSON
   let parentNum: Int?      // optional, because some values are null
   // ...
}

第二个问题是您的模型属性是 camelCased 而 JSON 是 snake_cased。 JSONDecoder 有一个 .convertFromSnakeCase startegy 来自动处理它。解码前需要在解码器上设置。

第三个问题是你需要解码根对象BGLCats,而不是BglCategory

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase // set the decoding strategy

let bglCats = try decoder.decode(BGLCats.self, from: data) // decode BGLCats

let blgCategories = bglCats.payload.bglCategory

【讨论】:

    【解决方案2】:

    问题在于 JSONDecoder 不知道例如 bglCategory 在 JSON 有效负载中表示为 bgl_category。如果 JSON 名称与您需要将 CodingKeys 实现到您的 Decodable 的变量名称不同

    在你的情况下:

    struct BglCategory: Decodable {
      
      let descriptionField : String
      let id : Int
      let name : String
      let number : String
      let parentNumber : Int?
      
      enum CodingKeys: String, CodingKey {
        case id, name, number
        case descriptionField = "description"
        case parentNumber = "parent_number"
      }
    }
    
    struct Payload: Decodable {
      
      let bglCategory : [BglCategory]!
      
      enum CodingKeys: String, CodingKey {
        case bglCategory = "bgl_category"
      }
      
    }
    

    【讨论】:

      猜你喜欢
      • 2018-10-21
      • 1970-01-01
      • 2021-08-07
      • 1970-01-01
      • 2017-11-16
      • 1970-01-01
      • 2021-07-31
      • 2018-08-06
      • 1970-01-01
      相关资源
      最近更新 更多