使用Alamofire调用CoinMarketCap API时数据解码失败求助
解决CoinMarketCap API数据解码失败问题
错误原因分析
你遇到的解码错误明确指向类型不匹配:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
核心问题是:JSON返回的data字段是数组类型(加密货币列表),但你的模型解析逻辑期望将其解析为单个字典对象,导致结构不匹配。
具体修复步骤
1. 修正模型层级的重复解析问题
你的ResponseObjectModel与BaseResponseModel存在重复处理data字段的冲突:
BaseResponseModel已经定义了data: [CurrencyResponse?],对应JSON的数组结构ResponseObjectModel又尝试将data映射为泛型T,而你请求时传入的T是单个CurrencyResponse对象,与实际数组类型不匹配
修复方案:简化请求解码逻辑
修改RequestManager的request方法,直接用BaseResponseModel作为顶层解析模型,移除ResponseObjectModel的冗余包装:
static func request(_ request: RequestDelegate, success: @escaping ObjectClosure<BaseResponseModel>, failure: @escaping ErrorClosure) { let request = createRequest(request) request.responseData { (response) in switch response.result { case .success: debugPrint("REQUEST: \(response.request)") guard let data = response.data else { handleFailure(response: response, failure: failure) return } do { let model = try JSONDecoder().decode(BaseResponseModel.self, from: data) success(model) } catch { print("解码失败: \(error)") handleFailure(response: response, failure: failure) } case .failure: handleFailure(response: response, failure: failure) } } }
2. 调整ViewModel的数据类型
ViewModel中存储数据的变量需要匹配数组类型:
final class AllCurrenciesViewModel: AllCurrenciesViewModelProtocol { public var didSuccess: ()->() = {} public var didFailure: (String)->() = {_ in} private var currenciesData: [CurrencyResponse]? // 改为数组类型 func requestAllCurrencies() { LastestCurrenciesRequest(start: 1, limit: 1, sort: "market_cap", currencyType: "all").request { [weak self] response in guard let self = self, let responseModel = response else { return } // 过滤数组中的nil元素 self.currenciesData = responseModel.data.compactMap { $0 } self.didSuccess() } failure: { (error) in self.didFailure(error.message ?? "") } } }
3. 修复CurrencyResponse的字段错误
你的模型中有一个拼写错误:queto应该对应JSON中的quote字段,同时补充完整的字段映射和关联模型:
struct CurrencyResponse: Codable { var id: Int? var name: String? var symbol: String? var slug: String? var cmcRank: Int? var numMarketPairs: Int? var circulatingSupply: Int? var totalSupply: Int? var maxSupply: Int? var quote: Quote? // 修正拼写错误 var lastUpdated: String? var dateAdded: String? var tags: [String?] // 明确定义字段映射关系 enum CodingKeys: String, CodingKey { case id, name, symbol, slug case cmcRank = "cmc_rank" case numMarketPairs = "num_market_pairs" case circulatingSupply = "circulating_supply" case totalSupply = "total_supply" case maxSupply = "max_supply" case quote case lastUpdated = "last_updated" case dateAdded = "date_added" case tags } } // 补充Quote关联模型 struct Quote: Codable { var usd: QuoteDetail? enum CodingKeys: String, CodingKey { case usd = "USD" } } struct QuoteDetail: Codable { var price: Double? var volume24h: Double? var volumeChange24h: Double? var percentChange1h: Double? var percentChange24h: Double? var percentChange7d: Double? var percentChange30d: Double? var percentChange60d: Double? var percentChange90d: Double? var marketCap: Double? var marketCapDominance: Double? var fullyDilutedMarketCap: Double? var lastUpdated: String? enum CodingKeys: String, CodingKey { case price case volume24h = "volume_24h" case volumeChange24h = "volume_change_24h" case percentChange1h = "percent_change_1h" case percentChange24h = "percent_change_24h" case percentChange7d = "percent_change_7d" case percentChange30d = "percent_change_30d" case percentChange60d = "percent_change_60d" case percentChange90d = "percent_change_90d" case marketCap = "market_cap" case marketCapDominance = "market_cap_dominance" case fullyDilutedMarketCap = "fully_diluted_market_cap" case lastUpdated = "last_updated" } }
4. 简化BaseResponseModel
移除自定义初始化逻辑,让Codable自动生成解码代码:
class BaseResponseModel: Codable { var status: CurrencyStatus? var data: [CurrencyResponse?] enum CodingKeys: String, CodingKey { case status, data } }
内容的提问来源于stack exchange,提问作者Bedri Doğan
相关产品推荐
相关产品推荐

