Swift中如何解码根层级为动态key的JSON并在SwiftUI中遍历?
解码失败问题解决
你遇到的解码报错核心原因是JSONDecoder默认的日期解码策略是将日期解析为UNIX时间戳(Double类型),但Hue Bridge返回的lastinstall是ISO8601格式的字符串,只需要在解码前配置解码器的日期策略即可:
let decoder = JSONDecoder() // 适配Hue返回的ISO8601格式日期 decoder.dateDecodingStrategy = .iso8601 // 如果系统自带的iso8601策略不匹配(比如部分旧版Hue固件日期不带时区),可以自定义日期格式: // let dateFormatter = DateFormatter() // dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" // dateFormatter.locale = Locale(identifier: "en_US_POSIX") // decoder.dateDecodingStrategy = .formatted(dateFormatter) let lights = try decoder.decode([String: LightInfo].self, from: data)
另外要注意如果JSON中部分字段可能缺失,建议把对应的属性声明为可选(比如var reachable: Bool?),避免单个字段缺失导致整个解码失败。
字典遍历优化方案
你当前的遍历写法可以简化,同时避免强制解包的风险,推荐两种更安全的写法:
方案1:转键值对数组遍历
直接将字典转成[(key: String, value: LightInfo)]类型的数组,用key作为唯一标识即可:
struct LightsView: View { @Binding var lights: [String:LightInfo] var body: some View { VStack { ForEach(Array(lights), id: \.key) { lightId, lightInfo in LightView(lightInfo: lightInfo, lightId: Int(lightId) ?? 0) } } } }
这种写法更直观,不需要额外处理keys枚举,也避免了强制解包lights["\(value)"]!的崩溃风险。
方案2:预处理成模型数组
如果不需要用到灯光ID的字符串key,可以在解码完成后直接把字典转成包含ID的模型数组,后续使用更方便:
// 提前定义带ID的模型 struct LightItem: Identifiable { let id: Int let info: LightInfo } // 解码后转换 let lightDict = try decoder.decode([String: LightInfo].self, from: data) let lights = lightDict.compactMap { key, info in guard let id = Int(key) else { return nil } return LightItem(id: id, info: info) } // 后续View中可以直接遍历 ForEach(lights) { light in LightView(lightInfo: light.info, lightId: light.id) }
这种写法符合SwiftUI对于Identifiable类型的遍历规范,代码更简洁。
内容的提问来源于stack exchange,提问作者Slawa
相关产品推荐
相关产品推荐

