如何将 DateFormatter 烘焙到 Swift 结构中,这样我就不需要一直告诉 JSONDecoder?
How do I bake DateFormatter into a Swift struct so that I don't keep needing to tell JSONDecoder?
我正在使用 Swift 4 解码来自 Twitter 的一些 JSON:
struct Tweet: Codable {
let id: String
let createdAt: Date
let text: String
enum CodingKeys: String, CodingKey {
case id = "id_str"
case createdAt = "created_at"
case text
}
}
let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let tweets = try decoder.decode([Tweet].self, from: data!)
我怎样才能让我的代码不必一直记住设置 decoder.dateDecodingStrategy
。理想情况下,Tweet
结构会知道其日期格式,并使用 dateFormatter
常量静态成员变量初始化为正确的格式。
我想我需要在 Tweet
上以某种方式使用 init(decoder: Decoder)
,但我不确定如何使用。
正如@Larme 在评论中所建议的那样,您可以将 JSONDecoder
子类化并覆盖其 init
方法,其中您将 dateDecodingStrategy
设置为 Twitter 的日期格式。您还应确保正确设置 DateFormatter
的 locale
,否则将无法正确解码 day/month 名称。我假设这些是英文的,所以我建议使用 Locale
en_US_POSIX
作为硬编码日期格式。
class JSONTweetDecoder: JSONDecoder {
private static let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
return dateFormatter
}()
override init() {
super.init()
self.dateDecodingStrategy = .formatted(JSONTweetDecoder.dateFormatter)
}
}
那么你只需要在解码响应时初始化一个 JSONTweetDecoder
而不是 JSONDecoder
。
let tweets = try JSONTweetDecoder().decode([Tweet].self, from: data!)
您可以扩展 Formatter 并创建自定义静态 DateFormatter。
extension Formatter {
static let custom: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
return formatter
}()
}
如果你想让 Tweet 解析你的日期字符串,你可以提供你自己的自定义解码器初始化程序,如下所示:
extension Tweet {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
text = try container.decode(String.self, forKey: .text)
createdAt = try Formatter.custom.date(from: container.decode(String.self, forKey: .createdAt))!
}
}
这假设你的日期字符串格式正确,如果你的日期字符串不能保证是正确的格式化程序,你可以使你的日期 属性 可选并从日期中删除强制解包(来自:字符串) 方法。
我正在使用 Swift 4 解码来自 Twitter 的一些 JSON:
struct Tweet: Codable {
let id: String
let createdAt: Date
let text: String
enum CodingKeys: String, CodingKey {
case id = "id_str"
case createdAt = "created_at"
case text
}
}
let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let tweets = try decoder.decode([Tweet].self, from: data!)
我怎样才能让我的代码不必一直记住设置 decoder.dateDecodingStrategy
。理想情况下,Tweet
结构会知道其日期格式,并使用 dateFormatter
常量静态成员变量初始化为正确的格式。
我想我需要在 Tweet
上以某种方式使用 init(decoder: Decoder)
,但我不确定如何使用。
正如@Larme 在评论中所建议的那样,您可以将 JSONDecoder
子类化并覆盖其 init
方法,其中您将 dateDecodingStrategy
设置为 Twitter 的日期格式。您还应确保正确设置 DateFormatter
的 locale
,否则将无法正确解码 day/month 名称。我假设这些是英文的,所以我建议使用 Locale
en_US_POSIX
作为硬编码日期格式。
class JSONTweetDecoder: JSONDecoder {
private static let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
return dateFormatter
}()
override init() {
super.init()
self.dateDecodingStrategy = .formatted(JSONTweetDecoder.dateFormatter)
}
}
那么你只需要在解码响应时初始化一个 JSONTweetDecoder
而不是 JSONDecoder
。
let tweets = try JSONTweetDecoder().decode([Tweet].self, from: data!)
您可以扩展 Formatter 并创建自定义静态 DateFormatter。
extension Formatter {
static let custom: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "eee MMM dd HH:mm:ss ZZZZ yyyy"
return formatter
}()
}
如果你想让 Tweet 解析你的日期字符串,你可以提供你自己的自定义解码器初始化程序,如下所示:
extension Tweet {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
text = try container.decode(String.self, forKey: .text)
createdAt = try Formatter.custom.date(from: container.decode(String.self, forKey: .createdAt))!
}
}
这假设你的日期字符串格式正确,如果你的日期字符串不能保证是正确的格式化程序,你可以使你的日期 属性 可选并从日期中删除强制解包(来自:字符串) 方法。