获取自定义类型的Date()

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了获取自定义类型的Date()相关的知识,希望对你有一定的参考价值。

我有一个服务器API,它以两种不同格式提供JSON日期-一个结构嵌套在另一个结构中。

我的Swift应用程序提取操作使用Swift Codable来解码JSON,因此我需要添加一些特殊处理以允许对两种自定义日期格式进行解码。

很方便,我发现此excellent guide可在一个结构中处理不同的格式。

struct FirstModel: Codable, Identifiable 
    var id: Int?
    let second: SecondModel
    let date: CustomDate<First>

    ...


struct SecondModel: Codable, Identifiable 
    var id: Int?
    let date: CustomDate<Second>

    ...

日期格式符定义如下,

struct CustomDate<E:HasDateFormatter>: Codable 

    let value: Date
    init(from decoder: Decoder) throws 
        let container = try decoder.singleValueContainer()
        let text = try container.decode(String.self)
        guard let date = E.dateFormatter.date(from: text) else 
            throw CustomDateError.general
        
        self.value = date
    
    enum CustomDateError: Error 
        case general
    


struct First: HasDateFormatter 
    static var dateFormatter: DateFormatter 
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale.init(identifier: Constants.localeUK)
        dateFormatter.dateFormat = "E, d MMM yyyy HH:mm zzz"
        return dateFormatter
    


struct Second: HasDateFormatter 
    static var dateFormatter: DateFormatter 
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale.init(identifier: Constants.localeUK)
        dateFormatter.dateFormat = "yyyy-MM-dd"
        return dateFormatter
    

这一切都很好。

我的问题在我想创建一个随后要编码的对象时开始。

我什至无法初始化结构,

let first = FirstModel(id: nil, date: Date())

编译器给出错误,

无法将'date'类型的值转换为'date'参数的预期参数类型'CustomDate'。

我知道CustomDate(xxx)是DateFormatter,所以不是Date类型,但是我什至不知道从哪里开始获取该'type'的新Date对象。请有人帮助以Swift编译器接受的方式传递当前日期的语法。谢谢。

PS。我也怀疑我在某个时候需要一个“设置”编码器,对吧?

UPDATE

按照下面的Joakim的建议,加上我尝试的编译器的建议的Fix,

let date = try! CustomDate<First>(from: Date() as! Decoder)

...可以编译,但是在运行时崩溃,并显示错误,

无法将类型'Foundation.Date'的值转换为'Swift.Decoder'。

答案

您可以创建自定义解码策略,以防失败时返回第二dateFormat。如果您需要按照相同的规则对自定义结构进行编码,则还需要提供自定义编码方法:

首先创建您的DateFormatters

extension Formatter 
    static let customUK: DateFormatter = 
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_UK")
        formatter.dateFormat = "E, d MMM yyyy HH:mm zzz"
        return formatter
    ()
    static let yyyyMMdd: DateFormatter = 
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_UK")
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter
    ()

然后是您的自定义日期解码策略

extension JSONDecoder.DateDecodingStrategy 
    static let customUK = custom 
        let container = try $0.singleValueContainer()
        let string = try container.decode(String.self)
        guard
            let date = Formatter.customUK.date(from: string) ??
                Formatter.yyyyMMdd.date(from: string) else 
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
        
        return date
    

以及您的自定义编码策略

extension JSONEncoder.DateEncodingStrategy 
    static let customUK = custom 
        var container = $1.singleValueContainer()
        try container.encode(Formatter.customUK.string(from: $0))
    

如果需要确保按照原始​​日期格式对子结构进行编码,则需要为其提供自己的编码方法:

struct First: Codable 
    var id: Int?
    let second: Second
    let date: Date
    init(id: Int?, date: Date = Date(), second: Second) 
        self.id = id
        self.date = date
        self.second = second
    


struct Second: Decodable 
    var id: Int?
    let date: Date
    init(id: Int?, date: Date = Date()) 
        self.id = id
        self.date = date
    


extension Second: Encodable 
    func encode(to encoder: Encoder) throws 
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(Formatter.yyyyMMdd.string(for: date), forKey: .date)
    


游乐场测试:

let first = First(id: 1, second: .init(id: 2))
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .customUK
let data = try! encoder.encode(first)

print(String(data: data, encoding: .utf8)!) // "id":1,"second":"id":2,"date":"2020-06-02","date":"Tue, 2 Jun 2020 16:31 GMT-3"

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .customUK
let firstDecoded = try! decoder.decode(First.self, from: data)
print(firstDecoded)  // "First(id: Optional(1), second: __lldb_expr_227.Second(id: Optional(2), date: 2020-06-02 03:00:00 +0000), date: 2020-06-02 19:38:00 +0000)\n"

以上是关于获取自定义类型的Date()的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC中自定义类型转换器(Date)

Spring MVC中自定义类型转换器(Date)

struts2---自定义类型转换器

SpringMVC 获得请求数据 -- 自定义类型转换器(Date)

类型转换

Date日期类型的绑定