Swift解析天气API JSON报错:期望字典却得数组,求排查解决
问题排查与解决方案
核心错误分析
- 解码目标类型不匹配:API返回的是JSON数组,但你最初尝试用单个
LocationData结构体解码,直接触发"Expected to decode Dictionary<String, Any> but found an array instead"错误。 - Typealias误用:你用
typealias LocationData = [LocationDatum]将LocationData重定义为数组类型,导致后续代码中访问LocationData.lat时报错——数组本身没有lat属性,该属性属于数组内的单个元素。 - 初始化器参数类型不匹配:当你尝试用
[LocationData].self解码出数组后,直接将整个数组传给需要单个LocationData对象的CurrentLocation初始化器,自然会出现类型转换错误。 - 隐藏的字段缺失问题:API返回的
local_names是空对象{},但你的LocalNames结构体强制要求ru和id字段,这会导致解码时因字段缺失失败。
修正后的完整代码
1. 适配API响应的Codable结构体
// 单个位置数据的结构体,对应JSON数组中的每一项 struct LocationDatum: Codable { let name: String let localNames: [String: String]? // 用可选字典替代固定字段结构体,适配空对象/多语言字段 let lat: Double let lon: Double enum CodingKeys: String, CodingKey { case name case localNames = "local_names" case lat case lon } }
- 把
LocalNames替换为[String: String]?:既可以处理空的local_names,也能兼容任意语言键值对,避免字段缺失导致的解码失败。
2. 修正CurrentLocation结构体
struct CurrentLocation { let latitude: Double // 修正拼写错误:lattitude → latitude let longitude: Double init?(locationData: LocationDatum) { self.latitude = locationData.lat self.longitude = locationData.lon } }
3. 正确的解码逻辑
// 假设data是API返回的Data对象 do { // 明确解码为数组类型 let locationArray = try JSONDecoder().decode([LocationDatum].self, from: data) // 从数组中取出目标元素(示例取第一个,可根据业务逻辑调整) if let targetLocation = locationArray.first { let currentLocation = CurrentLocation(locationData: targetLocation) // 后续使用currentLocation } } catch { print("解码失败:\(error)") }
关键修正说明
- 不要用typealias覆盖原有结构体:保留单个对象的
LocationDatum,解码时直接指定数组类型[LocationDatum].self即可,避免类型混淆。 - 数组需先提取元素再使用:解码得到的是数组,必须先取出单个
LocationDatum对象,才能传给CurrentLocation的初始化器。 - 灵活处理可选字段:API返回的字段可能为空或结构不固定,用可选字典替代强类型结构体更适配实际场景。
内容的提问来源于stack exchange,提问作者Nikita Chechnev
相关产品推荐
相关产品推荐

