考虑以下类型:

public struct DocumentDate: Codable {
    /// Year  component of the date, Integer with no limitation on the range.
    public let year: Int
    /// Month component of the date, Integer from 1 to 12.
    public let month: Int
    /// Day component of the date, Integer from 1 to 31.
    public let day: Int
}

Codable(更具体地说是Decodable)协议支持整合到此类型中以解码符合ISO 8601的日期类型的最简单方法是什么:

"2012-01-13"

理想情况下,我希望在初始化器中解码String,然后我就能够随心所欲地解析它.实施这种支持的最佳策略是什么?

我知道我可以使用dateDecodingStrategy,但那是将String解码为Date.我的类型实际上不是Date,而是定制的,所以这就是挑战所在.

推荐答案

我的建议是实现init(from:)并用Regular Exit解析日期字符串

public struct DocumentDate: Decodable {
    /// Year  component of the date, Integer with no limitation on the range.
    public let year: Int
    /// Month component of the date, Integer from 1 to 12.
    public let month: Int
    /// Day component of the date, Integer from 1 to 31.
    public let day: Int
    
    public init(from decoder: any Decoder) throws {
        let container = try decoder.singleValueContainer()
        let dateString = try container.decode(String.self)
        guard let match = dateString.wholeMatch(of: /(\d{4})-(\d{2})-(\d{2})/)?.output else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "\(dateString) is not a valid date format")
        }
        year = Int(match.1)!
        month = Int(match.2)!
        day = Int(match.3)!
    }
}

以及如何使用它的示例

let jsonString = """
{"date":"2012-01-13"}
"""

struct Test: Decodable {
    let date: DocumentDate
}

do {
    let result = try JSONDecoder().decode(Test.self, from: Data(jsonString.utf8))
    print(result)
} catch {
    print(error)
}

例如,您甚至可以判断月和日是否在有效范围内

let month = Int(match.2)!
guard (1...12).contains(month) else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "Month value \(month) is out of range") }
self.month = month

另一种方法是创建一个包含ISO8601DateFormatter的日期并提取所需的日期成分

public struct DocumentDate: Decodable {
    /// Year  component of the date, Integer with no limitation on the range.
    public let year: Int
    /// Month component of the date, Integer from 1 to 12.
    public let month: Int
    /// Day component of the date, Integer from 1 to 31.
    public let day: Int
    
    public init(from decoder: any Decoder) throws {
        let container = try decoder.singleValueContainer()
        let dateString = try container.decode(String.self)
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = .withFullDate
        guard let date = formatter.date(from: dateString) else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "\(dateString) is not a valid date format")
        }
        let components = Calendar(identifier: .iso8601).dateComponents([.year, .month, .day], from: date)
        year = components.year!
        month = components.month!
        day = components.day!
    }
}

Swift相关问答推荐

TabView中的视频播放器意外播放

为什么我不能在这个 Swift 间接枚举中返回 self ?

是否可以限制 ExpressibleByIntegerLiteral struct 的初始值?

SwiftUI 中的同一个 ForEach 中是否可以有 2 个数组?

在Swift中如何用变量计算0.9的立方

为什么我的Swift app不能用xcodebuild构建,但是用XCode可以构建?

Swiftui 无法从核心数据中获取数据

类型 '()' 不能符合 View (除非它肯定是 View,这次没有恶作剧)

Swift Components.Url 返回错误的 URL

将阻塞函数集成到 Swift 异步中

从 Finder 打开文件时,SwiftUI 的应用程序(_ openFile:) 从未调用过

符合协议要求委托变量在 ios13 中可用

无法从自定义动态框架(Swift)访问类

如何在界面生成器中创建 UILayoutGuide?

如何实现 Swift 的 Comparable 协议?

Swift:如何从函数返回类类型

自动布局:获取 UIImageView 高度以正确计算单元格高度

UISearchbar TextField 的高度可以修改吗?

类型myViewController不符合 Swift 中的协议 UIPIckerDataSource

swift - 我可以从我的自定义初始化方法中调用 struct 默认成员初始化吗?