You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为自定义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)")
    }
}

代码说明

  1. 解码逻辑:
    • 通过singleValueContainer()获取解码器中的单值容器,直接读取日期字符串
    • 用-拆分字符串,验证组件数量是否为3
    • 将组件转换为整数,并校验月、日的取值范围,不符合则抛出标准DecodingError
  2. 编码逻辑(可选):
    • 将年、月、日格式化为标准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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 17:42:33