Swift中如何通过Decodable对象将任意JSON值解码为字符串类型
解决方案
你遇到的问题是Swift内置的JSONDecoder默认严格匹配JSON类型,数值类型(整数、浮点数)无法直接解码为String类型属性,以下是两种可直接落地的实现方案:
方案1:手动实现Decodable初始化(仅当前结构体适用)
适合只有单个结构体需要该转换规则的场景,核心逻辑是对每个字段尝试解码不同类型,统一转为字符串存储,避免精度丢失:
struct StockNFS: Decodable { let name: String? let price: String? let no: String? let id: String? let amount: String? enum CodingKeys: String, CodingKey { case name, price, no, id, amount } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decodeIfPresent(String.self, forKey: .name) price = try container.decodeStringLossless(forKey: .price) no = try container.decodeStringLossless(forKey: .no) id = try container.decodeIfPresent(String.self, forKey: .id) amount = try container.decodeStringLossless(forKey: .amount) } } // 扩展解码容器复用转换逻辑 extension KeyedDecodingContainer { func decodeStringLossless(forKey key: Key) throws -> String? { // 优先直接取字符串 if let str = try? decodeIfPresent(String.self, forKey: key) { return str } // 尝试取整数转字符串 if let intVal = try? decodeIfPresent(Int.self, forKey: key) { return String(intVal) } // 尝试取十进制数转字符串(精度远高于Double,适合超长数值场景) if let decimalVal = try? decodeIfPresent(Decimal.self, forKey: key) { return NSDecimalNumber(decimal: decimalVal).stringValue } return nil } }
方案2:属性包装器实现(多结构体复用场景)
如果项目中多个结构体都需要统一转字符串的解码规则,可以封装属性包装器减少重复代码:
@propertyWrapper struct StringLossless: Decodable { var wrappedValue: String? init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let str = try? container.decode(String.self) { wrappedValue = str } else if let intVal = try? container.decode(Int.self) { wrappedValue = String(intVal) } else if let decimalVal = try? container.decode(Decimal.self) { wrappedValue = NSDecimalNumber(decimal: decimalVal).stringValue } else { wrappedValue = nil } } } // 使用时只需给属性加包装器,无需自定义初始化方法 struct StockNFS: Decodable { @StringLossless var name: String? @StringLossless var price: String? @StringLossless var no: String? @StringLossless var id: String? @StringLossless var amount: String? }
两种方案都完全避免了浮点数存储导致的精度异常问题,拿到的字符串可以直接用于UI展示,不需要额外转换。
内容的提问来源于stack exchange,提问作者Tar journey
相关产品推荐
相关产品推荐

