如何为自定义DocumentDate结构体实现Decodable以解码ISO 8601日期字符串?
自定义DocumentDate结构体的Codable(Decodable)实现:适配ISO 8601日期字符串解码
问题背景
现有如下自定义Swift结构体:
public struct DocumentDate: Codable { /// 日期的年份组件,无范围限制的整数 public let year: Int /// 日期的月份组件,取值1-12的整数 public let month: Int /// 日期的日组件,取值1-31的整数 public let day: Int }
需要为该类型添加Codable(重点是Decodable)支持,实现解码形如"2012-01-13"的ISO 8601合规日期字符串,要求在初始化器中先解码得到字符串再自行解析。已知dateDecodingStrategy仅适用于Date类型,自定义类型需手动实现解码逻辑。
解决方案
由于DocumentDate是自定义结构体,无法复用JSONDecoder的日期解码策略,需手动实现Decodable协议的初始化方法,同时可按需自定义Encodable逻辑以输出标准格式字符串。
完整代码实现
public struct DocumentDate: Codable { public let year: Int public let month: Int public let day: Int // MARK: - 自定义Decodable初始化 public init(from decoder: Decoder) throws { // 获取单值容器,读取日期字符串 let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) // 拆分字符串为年、月、日组件 let components = dateString.split(separator: "-") guard components.count == 3 else { throw DecodingError.dataCorruptedError( in: container, debugDescription: "日期格式不符合ISO 8601要求,需为'YYYY-MM-DD'格式" ) } // 转换组件为整数,并验证合法性 guard let year = Int(components[0]), let month = Int(components[1]), let day = Int(components[2]) else { throw DecodingError.dataCorruptedError( in: container, debugDescription: "日期组件无法转换为有效整数" ) } guard (1...12).contains(month) else { throw DecodingError.dataCorruptedError( in: container, debugDescription: "月份取值需在1-12范围内" ) } guard (1...31).contains(day) else { throw DecodingError.dataCorruptedError( in: container, debugDescription: "日期取值需在1-31范围内" ) } self.year = year self.month = month self.day = day } // MARK: - 自定义Encodable实现(可选,保证输出标准ISO格式) public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() // 格式化月、日为两位数,确保输出格式统一 let formattedMonth = String(format: "%02d", month) let formattedDay = String(format: "%02d", day) try container.encode("\(year)-\(formattedMonth)-\(formattedDay)") } }
代码说明
- 解码逻辑:
- 通过
singleValueContainer()获取解码器中的单值容器,直接读取日期字符串 - 用
-拆分字符串,验证组件数量是否为3 - 将组件转换为整数,并校验月、日的取值范围,不符合则抛出标准
DecodingError
- 通过
- 编码逻辑(可选):
- 将年、月、日格式化为标准ISO 8601字符串,保证编码输出的一致性
使用示例
// 解码示例 let json = #"{"documentDate": "2012-01-13"}"# let data = json.data(using: .utf8)! do { let decoder = JSONDecoder() let result = try decoder.decode([String: DocumentDate].self, from: data) print(result["documentDate"]!) // 输出:DocumentDate(year: 2012, month: 1, day: 13) } catch { print("解码错误:\(error)") } // 编码示例 let docDate = DocumentDate(year: 2024, month: 5, day: 8) do { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let data = try encoder.encode(["documentDate": docDate]) print(String(data: data, encoding: .utf8)!) // 输出: // { // "documentDate" : "2024-05-08" // } } catch { print("编码错误:\(error)") }
内容的提问来源于stack exchange,提问作者Richard Topchii
相关产品推荐
相关产品推荐

