Swift中车辆JSON响应的History模型构建与TableView展示方案
解决TableView中车辆数据History字段多类型的问题
我来帮你一步步搞定这个API返回多类型字段的问题,分三个核心部分梳理解决方案:
1. 修改VehicleDataModel的History结构
首先得调整模型来适配history的两种类型(数组/字符串"None")。原来的模型里history是必填数组,这显然扛不住API返回字符串的情况,所以咱们把它改成可选数组,同时给模型加上Codable协议支持,再通过自定义解码逻辑来处理两种类型的转换:
struct VehicleDataModel: Codable { let registrationNo: String? let engineCC: String? let engineCCID: String? let vehicleID: String? let history: [HistoryModel]? // 改成可选数组,适配"None"场景 struct HistoryModel: Codable { let packages: String? let billDiscount: String? let dateTime: String? let billPaid: String? let billAmount: String? // 映射API的蛇形命名字段 enum CodingKeys: String, CodingKey { case packages case billDiscount = "bill_discount" case dateTime = "date_time" case billPaid = "bill_paid" case billAmount = "bill_amount" } } // 映射VehicleDataModel的字段 enum CodingKeys: String, CodingKey { case registrationNo = "registration_no" case engineCC = "engincc" case engineCCID = "enginccID" case vehicleID = "vehicleID" case history } // 自定义解码逻辑,处理history的两种类型 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) registrationNo = try container.decodeIfPresent(String.self, forKey: .registrationNo) engineCC = try container.decodeIfPresent(String.self, forKey: .engineCC) engineCCID = try container.decodeIfPresent(String.self, forKey: .engineCCID) vehicleID = try container.decodeIfPresent(String.self, forKey: .vehicleID) // 先尝试解码成数组,失败则处理字符串 do { history = try container.decodeIfPresent([HistoryModel].self, forKey: .history) } catch { let historyStr = try container.decode(String.self, forKey: .history) // 如果是"None"就设为nil,也可以改成空数组,看你需求 history = historyStr == "None" ? nil : nil } } } // 别忘了定义顶层响应模型,对应API的整体结构 struct VehicleResponse: Codable { let response: String? let accountType: String? let carData: [VehicleDataModel]? enum CodingKeys: String, CodingKey { case response case accountType = "account_type" case carData = "car_data" } }
2. 通过Alamofire完成数据映射
推荐用Codable配合Alamofire的responseDecodable自动解析,比手动抠字段靠谱多了,代码也简洁:
func callApi() { let url = "http://esspk.net/production/20m/Api/getVehicleApi" let userID = UserDefaults.standard.integer(forKey: "user_id") let param = ["user_id": userID] AF.request(url, method: .post, parameters: param) .validate() // 自动校验HTTP状态码 .responseDecodable(of: VehicleResponse.self) { response in switch response.result { case .success(let res): // 拿到车辆数据数组,赋值给数据源 if let carList = res.carData { self.vehicalModel = carList // 主线程刷新TableView DispatchQueue.main.async { self.myVehicleTblView.reloadData() } } case .failure(let error): print("API调用翻车了:\(error.localizedDescription)") } } }
要是你想保留原来的手动解析方式(虽然不推荐),可以这么处理history字段:
func callApi() { let url = "http://esspk.net/production/20m/Api/getVehicleApi" let userID = UserDefaults.standard.integer(forKey: "user_id") let param = ["user_id" : userID] print(param) ServerCall.makeCallWitoutFile(url, params: param, type: Method.POST, currentView: nil) { (response) in if let json = response { print(json) if let carData = json["car_data"].array { var vehicleList = [VehicleDataModel]() for cData in carData { let regNo = cData["registration_no"].string let enginCC = cData["engincc"].string let enginID = cData["enginccID"].string let vehicleID = cData["vehicleID"].string var history: [VehicleDataModel.HistoryModel]? = nil // 判断history的类型 if let historyArr = cData["history"].array { // 是数组,转成HistoryModel列表 var historyModels = [VehicleDataModel.HistoryModel]() for item in historyArr { let pkg = item["packages"].string ?? "" let discount = item["bill_discount"].string ?? "" let date = item["date_time"].string ?? "" let paid = item["bill_paid"].string ?? "" let amount = item["bill_amount"].string ?? "" let model = VehicleDataModel.HistoryModel(packages: pkg, billDiscount: discount, dateTime: date, billPaid: paid, billAmount: amount) historyModels.append(model) } history = historyModels } else if let historyStr = cData["history"].string, historyStr == "None" { // 是"None"字符串,设为nil history = nil } let vehicle = VehicleDataModel(registrationNo: regNo, engineCC: enginCC, engineCCID: enginID, vehicleID: vehicleID, history: history) vehicleList.append(vehicle) } self.vehicalModel = vehicleList DispatchQueue.main.async { self.myVehicleTblView.reloadData() } } } } }
3. 判断History是否存在并在TableView展示
在TableView的数据源方法里,判断当前车辆的history是否存在,然后展示对应的内容就行:
// 假设你的自定义Cell有显示车辆信息和历史记录的标签 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "VehicleCell", for: indexPath) as! VehicleCell let vehicle = vehicalModel[indexPath.row] // 填充车辆基本信息 cell.regNoLabel.text = vehicle.registrationNo ?? "无车牌" cell.engineCCLabel.text = vehicle.engineCC ?? "未知排量" // 处理历史记录展示 if let history = vehicle.history, !history.isEmpty { // 有历史记录,显示数量或第一条详情 cell.historyStatusLabel.text = "历史记录:\(history.count)条" let firstRecord = history[0] cell.historyDetailLabel.text = "最近一次:\(firstRecord.dateTime ?? ""),金额:\(firstRecord.billAmount ?? "")" } else { // 无历史记录 cell.historyStatusLabel.text = "无历史记录" cell.historyDetailLabel.text = "" } return cell }
如果需要展开显示所有历史记录,可以在didSelectRowAt里处理,或者用TableView的折叠功能,这里就不展开讲啦。
内容的提问来源于stack exchange,提问作者Mike Shaen
相关产品推荐
相关产品推荐

