SwiftUI使用带枚举的Codable解析JSON后无法提取实际值如何解决?
解决方案
你自定义的Datum是带关联值的Swift枚举,要取出内部存储的值可以用以下3种常见方案,按使用便捷度排序:
方案1:给枚举增加便捷取值属性(最推荐)
直接在Datum枚举定义中增加两个计算属性,后续取值无需每次写匹配逻辑:
enum Datum: Codable { case dInteger(Int) case dString(String) // 新增以下两个计算属性 var stringValue: String? { guard case .dString(let value) = self else { return nil } return value } var intValue: Int? { guard case .dInteger(let value) = self else { return nil } return value } // 原有init和encode逻辑保持不变 init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let x = try? container.decode(Int.self) { self = .dInteger(x) return } if let x = try? container.decode(String.self) { self = .dString(x) return } throw DecodingError.typeMismatch(Datum.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Datum")) } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .dInteger(let x): try container.encode(x) case .dString(let x): try container.encode(x) } } }
使用时直接调用属性即可:
// 取时间字符串 let timeStr = dataStore.topLevels[0].series[0].data[0][0].stringValue ?? "" // 取数值 let demandValue = dataStore.topLevels[0].series[0].data[0][1].intValue ?? 0
方案2:临时匹配枚举值
不需要修改枚举定义,单次取值时直接用if case/switch匹配关联值:
// 取第一个元素的字符串值 let firstDatum = dataStore.topLevels[0].series[0].data[0][0] if case .dString(let time) = firstDatum { print(time) // 输出 20210522T04Z } // 取第二个元素的整数值 let secondDatum = dataStore.topLevels[0].series[0].data[0][1] if case .dInteger(let value) = secondDatum { print(value) // 输出 438877 }
方案3:优化解析逻辑直接生成可用结构体(适合长期维护)
由于你的data子数组结构固定:第0位是时间字符串、第1位是需求数值,你可以直接在解析层就把二维枚举数组转成业务结构体,后续完全不需要处理枚举:
- 先定义业务结构体:
struct DemandPoint: Codable { let time: String let value: Int }
- 自定义
DemandPoint的解析逻辑:
extension DemandPoint { init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() // 解析第一个元素为字符串 time = try container.decode(String.self) // 解析第二个元素为整数 value = try container.decode(Int.self) } }
- 修改
Series的data属性定义:
struct Series: Codable { let seriesID, name: String // 把原来的[[Datum]]改成[DemandPoint] let data: [DemandPoint] enum CodingKeys: String, CodingKey { case seriesID = "series_id" case name, data } }
修改后解析出来的data直接是DemandPoint数组,取值更简单:
let point = dataStore.topLevels[0].series[0].data[0] print(point.time) // 直接输出时间字符串 print(point.value) // 直接输出数值
内容的提问来源于stack exchange,提问作者JohnSF
相关产品推荐
相关产品推荐

