Swift 的 JSON 解码器在 JSON 字符串中具有多种日期格式?
Swift's JSONDecoder with multiple date formats in a JSON string?
Swift 的 JSONDecoder
提供了一个 dateDecodingStrategy
属性,它允许我们定义如何根据 DateFormatter
解释传入的日期字符串对象。
但是,我目前正在使用 API returns 日期字符串 (yyyy-MM-dd
) 和日期时间字符串 (yyyy-MM-dd HH:mm:ss
),具体取决于 属性。有没有办法让 JSONDecoder
处理这个问题,因为提供的 DateFormatter
对象一次只能处理一个 dateFormat
?
一个拙劣的解决方案是重写附带的 Decodable
模型以仅接受字符串作为它们的属性并提供 public Date
getter/setter 变量,但是对我来说似乎是一个糟糕的解决方案。有什么想法吗?
使用单个编码器无法做到这一点。您最好的选择是自定义 encode(to encoder:)
和 init(from decoder:)
方法并为这些值中的一个提供您自己的翻译,为另一个保留内置日期策略。
为此可能值得考虑将一个或多个格式化程序传递到 userInfo
对象中。
有几种方法可以解决这个问题:
- 您可以创建一个
DateFormatter
子类,它首先尝试日期时间字符串格式,如果失败,则尝试纯日期格式
- 你可以给出一个
.custom
Date
解码策略,其中你向 Decoder
请求 singleValueContainer()
,解码一个字符串,然后将它传递给你想要的任何格式化程序在将解析日期传递出去之前
- 您可以围绕
Date
类型创建一个包装器,它提供了一个自定义 init(from:)
和 encode(to:)
来执行此操作(但这实际上并不比 .custom
策略)
- 您可以按照您的建议使用纯字符串
- 您可以为使用这些日期的所有类型提供自定义
init(from:)
并在其中尝试不同的事情
总而言之,前两种方法可能是最简单和最干净的——您将在任何地方保留 Codable
的默认合成实现,而不会牺牲类型安全性。
请尝试配置与此类似的解码器:
lazy var decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
let container = try decoder.singleValueContainer()
let dateStr = try container.decode(String.self)
// possible date strings: "2016-05-01", "2016-07-04T17:37:21.119229Z", "2018-05-20T15:00:00Z"
let len = dateStr.count
var date: Date? = nil
if len == 10 {
date = dateNoTimeFormatter.date(from: dateStr)
} else if len == 20 {
date = isoDateFormatter.date(from: dateStr)
} else {
date = self.serverFullDateFormatter.date(from: dateStr)
}
guard let date_ = date else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateStr)")
}
print("DATE DECODER \(dateStr) to \(date_)")
return date_
})
return decoder
}()
面对同样的问题,我写了以下扩展:
extension JSONDecoder.DateDecodingStrategy {
static func custom(_ formatterForKey: @escaping (CodingKey) throws -> DateFormatter?) -> JSONDecoder.DateDecodingStrategy {
return .custom({ (decoder) -> Date in
guard let codingKey = decoder.codingPath.last else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "No Coding Path Found"))
}
guard let container = try? decoder.singleValueContainer(),
let text = try? container.decode(String.self) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date text"))
}
guard let dateFormatter = try formatterForKey(codingKey) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "No date formatter for date text")
}
if let date = dateFormatter.date(from: text) {
return date
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(text)")
}
})
}
}
此扩展允许您为 JSON 解码器创建一个 DateDecodingStrategy,它在同一个 JSON 字符串中处理多种不同的日期格式。该扩展包含一个函数,该函数需要实现一个为您提供 CodingKey 的闭包,您需要为所提供的密钥提供正确的 DateFormatter。
假设您有以下 JSON:
{
"publication_date": "2017-11-02",
"opening_date": "2017-11-03",
"date_updated": "2017-11-08 17:45:14"
}
以下结构:
struct ResponseDate: Codable {
var publicationDate: Date
var openingDate: Date?
var dateUpdated: Date
enum CodingKeys: String, CodingKey {
case publicationDate = "publication_date"
case openingDate = "opening_date"
case dateUpdated = "date_updated"
}
}
然后要解码 JSON,您将使用以下代码:
let dateFormatterWithTime: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter
}()
let dateFormatterWithoutTime: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom({ (key) -> DateFormatter? in
switch key {
case ResponseDate.CodingKeys.publicationDate, ResponseDate.CodingKeys.openingDate:
return dateFormatterWithoutTime
default:
return dateFormatterWithTime
}
})
let results = try? decoder.decode(ResponseDate.self, from: data)
如果您在单个模型中有多个不同格式的日期,则很难为每个日期应用 .dateDecodingStrategy
。
在此处查看 https://gist.github.com/romanroibu/089ec641757604bf78a390654c437cb0 以获得方便的解决方案
试试这个。 (swift 4)
let formatter = DateFormatter()
var decoder: JSONDecoder {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let date = formatter.date(from: dateString) {
return date
}
formatter.dateFormat = "yyyy-MM-dd"
if let date = formatter.date(from: dateString) {
return date
}
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Cannot decode date string \(dateString)")
}
return decoder
}
有点冗长,但更灵活的方法:用另一个日期包装日期 class,并为其实现自定义序列化方法。例如:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
class MyCustomDate: Codable {
var date: Date
required init?(_ date: Date?) {
if let date = date {
self.date = date
} else {
return nil
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let string = dateFormatter.string(from: date)
try container.encode(string)
}
required public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let raw = try container.decode(String.self)
if let date = dateFormatter.date(from: raw) {
self.date = date
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot parse date")
}
}
}
所以现在您独立于 .dateDecodingStrategy
和 .dateEncodingStrategy
并且您的 MyCustomDate
日期将以指定格式解析。在 class:
中使用
class User: Codable {
var dob: MyCustomDate
}
用
实例化
user.dob = MyCustomDate(date)
Swift 5
实际上基于使用 JSONDecoder
扩展名的@BrownsooHan 版本
JSON解码器+dateDecodingStrategyFormatters.swift
extension JSONDecoder {
/// Assign multiple DateFormatter to dateDecodingStrategy
///
/// Usage :
///
/// decoder.dateDecodingStrategyFormatters = [ DateFormatter.standard, DateFormatter.yearMonthDay ]
///
/// The decoder will now be able to decode two DateFormat, the 'standard' one and the 'yearMonthDay'
///
/// Throws a 'DecodingError.dataCorruptedError' if an unsupported date format is found while parsing the document
var dateDecodingStrategyFormatters: [DateFormatter]? {
@available(*, unavailable, message: "This variable is meant to be set only")
get { return nil }
set {
guard let formatters = newValue else { return }
self.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
for formatter in formatters {
if let date = formatter.date(from: dateString) {
return date
}
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)")
}
}
}
}
添加一个只能设置的变量的方法有点hacky,但是你可以很容易的把var dateDecodingStrategyFormatters
改成func setDateDecodingStrategyFormatters(_ formatters: [DateFormatter]? )
用法
假设您已经在代码中定义了多个 DateFormatter
,如下所示:
extension DateFormatter {
static let standardT: DateFormatter = {
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
return dateFormatter
}()
static let standard: DateFormatter = {
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return dateFormatter
}()
static let yearMonthDay: DateFormatter = {
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter
}()
}
您现在可以通过设置 dateDecodingStrategyFormatters
直接将这些分配给解码器:
// Data structure
struct Dates: Codable {
var date1: Date
var date2: Date
var date3: Date
}
// The Json to decode
let jsonData = """
{
"date1": "2019-05-30 15:18:00",
"date2": "2019-05-30T05:18:00",
"date3": "2019-04-17"
}
""".data(using: .utf8)!
// Assigning mutliple DateFormatters
let decoder = JSONDecoder()
decoder.dateDecodingStrategyFormatters = [ DateFormatter.standardT,
DateFormatter.standard,
DateFormatter.yearMonthDay ]
do {
let dates = try decoder.decode(Dates.self, from: jsonData)
print(dates)
} catch let err as DecodingError {
print(err.localizedDescription)
}
旁注
我再次意识到将 dateDecodingStrategyFormatters
设置为 var
有点老套,我不推荐这样做,您应该定义一个函数。然而,这样做是个人偏好。
向 KeyedDecodingContainer 添加扩展
extension KeyedDecodingContainer {
func decodeDate(forKey key: KeyedDecodingContainer<K>.Key, withPossible formats: [DateFormatter]) throws -> Date? {
for format in formats {
if let date = format.date(from: try self.decode(String.self, forKey: key)) {
return date
}
}
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Date string does not match format expected by formatter.")
}
}
并使用'try container.decodeDate(forKey: 'key', withPossible: [.iso8601Full, .yyyyMMdd])'
完整的解决方案在这里:
import Foundation
extension DateFormatter {
static let iso8601Full: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.calendar = Calendar(identifier: .iso8601)
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
static let yyyyMMdd: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.calendar = Calendar(identifier: .iso8601)
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
}
public struct RSSFeed: Codable {
public let releaseDate: Date?
public let releaseDateAndTime: Date?
}
extension RSSFeed {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
releaseDate = try container.decodeDate(forKey: .releaseDate, withPossible: [.iso8601Full, .yyyyMMdd])
releaseDateAndTime = try container.decodeDate(forKey: .releaseDateAndTime, withPossible: [.iso8601Full, .yyyyMMdd])
}
}
extension KeyedDecodingContainer {
func decodeDate(forKey key: KeyedDecodingContainer<K>.Key, withPossible formats: [DateFormatter]) throws -> Date? {
for format in formats {
if let date = format.date(from: try self.decode(String.self, forKey: key)) {
return date
}
}
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Date string does not match format expected by formatter.")
}
}
let json = """
{
"releaseDate":"2017-11-12",
"releaseDateAndTime":"2017-11-16 02:02:55"
}
"""
let data = Data(json.utf8)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601Full)
let rssFeed = try! decoder.decode(RSSFeed.self, from: data)
let feed = rssFeed
print(feed.releaseDate, feed.releaseDateAndTime)
Swift 的 JSONDecoder
提供了一个 dateDecodingStrategy
属性,它允许我们定义如何根据 DateFormatter
解释传入的日期字符串对象。
但是,我目前正在使用 API returns 日期字符串 (yyyy-MM-dd
) 和日期时间字符串 (yyyy-MM-dd HH:mm:ss
),具体取决于 属性。有没有办法让 JSONDecoder
处理这个问题,因为提供的 DateFormatter
对象一次只能处理一个 dateFormat
?
一个拙劣的解决方案是重写附带的 Decodable
模型以仅接受字符串作为它们的属性并提供 public Date
getter/setter 变量,但是对我来说似乎是一个糟糕的解决方案。有什么想法吗?
使用单个编码器无法做到这一点。您最好的选择是自定义 encode(to encoder:)
和 init(from decoder:)
方法并为这些值中的一个提供您自己的翻译,为另一个保留内置日期策略。
为此可能值得考虑将一个或多个格式化程序传递到 userInfo
对象中。
有几种方法可以解决这个问题:
- 您可以创建一个
DateFormatter
子类,它首先尝试日期时间字符串格式,如果失败,则尝试纯日期格式 - 你可以给出一个
.custom
Date
解码策略,其中你向Decoder
请求singleValueContainer()
,解码一个字符串,然后将它传递给你想要的任何格式化程序在将解析日期传递出去之前 - 您可以围绕
Date
类型创建一个包装器,它提供了一个自定义init(from:)
和encode(to:)
来执行此操作(但这实际上并不比.custom
策略) - 您可以按照您的建议使用纯字符串
- 您可以为使用这些日期的所有类型提供自定义
init(from:)
并在其中尝试不同的事情
总而言之,前两种方法可能是最简单和最干净的——您将在任何地方保留 Codable
的默认合成实现,而不会牺牲类型安全性。
请尝试配置与此类似的解码器:
lazy var decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
let container = try decoder.singleValueContainer()
let dateStr = try container.decode(String.self)
// possible date strings: "2016-05-01", "2016-07-04T17:37:21.119229Z", "2018-05-20T15:00:00Z"
let len = dateStr.count
var date: Date? = nil
if len == 10 {
date = dateNoTimeFormatter.date(from: dateStr)
} else if len == 20 {
date = isoDateFormatter.date(from: dateStr)
} else {
date = self.serverFullDateFormatter.date(from: dateStr)
}
guard let date_ = date else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateStr)")
}
print("DATE DECODER \(dateStr) to \(date_)")
return date_
})
return decoder
}()
面对同样的问题,我写了以下扩展:
extension JSONDecoder.DateDecodingStrategy {
static func custom(_ formatterForKey: @escaping (CodingKey) throws -> DateFormatter?) -> JSONDecoder.DateDecodingStrategy {
return .custom({ (decoder) -> Date in
guard let codingKey = decoder.codingPath.last else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "No Coding Path Found"))
}
guard let container = try? decoder.singleValueContainer(),
let text = try? container.decode(String.self) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not decode date text"))
}
guard let dateFormatter = try formatterForKey(codingKey) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "No date formatter for date text")
}
if let date = dateFormatter.date(from: text) {
return date
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(text)")
}
})
}
}
此扩展允许您为 JSON 解码器创建一个 DateDecodingStrategy,它在同一个 JSON 字符串中处理多种不同的日期格式。该扩展包含一个函数,该函数需要实现一个为您提供 CodingKey 的闭包,您需要为所提供的密钥提供正确的 DateFormatter。
假设您有以下 JSON:
{
"publication_date": "2017-11-02",
"opening_date": "2017-11-03",
"date_updated": "2017-11-08 17:45:14"
}
以下结构:
struct ResponseDate: Codable {
var publicationDate: Date
var openingDate: Date?
var dateUpdated: Date
enum CodingKeys: String, CodingKey {
case publicationDate = "publication_date"
case openingDate = "opening_date"
case dateUpdated = "date_updated"
}
}
然后要解码 JSON,您将使用以下代码:
let dateFormatterWithTime: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter
}()
let dateFormatterWithoutTime: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom({ (key) -> DateFormatter? in
switch key {
case ResponseDate.CodingKeys.publicationDate, ResponseDate.CodingKeys.openingDate:
return dateFormatterWithoutTime
default:
return dateFormatterWithTime
}
})
let results = try? decoder.decode(ResponseDate.self, from: data)
如果您在单个模型中有多个不同格式的日期,则很难为每个日期应用 .dateDecodingStrategy
。
在此处查看 https://gist.github.com/romanroibu/089ec641757604bf78a390654c437cb0 以获得方便的解决方案
试试这个。 (swift 4)
let formatter = DateFormatter()
var decoder: JSONDecoder {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if let date = formatter.date(from: dateString) {
return date
}
formatter.dateFormat = "yyyy-MM-dd"
if let date = formatter.date(from: dateString) {
return date
}
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Cannot decode date string \(dateString)")
}
return decoder
}
有点冗长,但更灵活的方法:用另一个日期包装日期 class,并为其实现自定义序列化方法。例如:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
class MyCustomDate: Codable {
var date: Date
required init?(_ date: Date?) {
if let date = date {
self.date = date
} else {
return nil
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let string = dateFormatter.string(from: date)
try container.encode(string)
}
required public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let raw = try container.decode(String.self)
if let date = dateFormatter.date(from: raw) {
self.date = date
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot parse date")
}
}
}
所以现在您独立于 .dateDecodingStrategy
和 .dateEncodingStrategy
并且您的 MyCustomDate
日期将以指定格式解析。在 class:
class User: Codable {
var dob: MyCustomDate
}
用
实例化user.dob = MyCustomDate(date)
Swift 5
实际上基于使用 JSONDecoder
扩展名的@BrownsooHan 版本
JSON解码器+dateDecodingStrategyFormatters.swift
extension JSONDecoder {
/// Assign multiple DateFormatter to dateDecodingStrategy
///
/// Usage :
///
/// decoder.dateDecodingStrategyFormatters = [ DateFormatter.standard, DateFormatter.yearMonthDay ]
///
/// The decoder will now be able to decode two DateFormat, the 'standard' one and the 'yearMonthDay'
///
/// Throws a 'DecodingError.dataCorruptedError' if an unsupported date format is found while parsing the document
var dateDecodingStrategyFormatters: [DateFormatter]? {
@available(*, unavailable, message: "This variable is meant to be set only")
get { return nil }
set {
guard let formatters = newValue else { return }
self.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
for formatter in formatters {
if let date = formatter.date(from: dateString) {
return date
}
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)")
}
}
}
}
添加一个只能设置的变量的方法有点hacky,但是你可以很容易的把var dateDecodingStrategyFormatters
改成func setDateDecodingStrategyFormatters(_ formatters: [DateFormatter]? )
用法
假设您已经在代码中定义了多个 DateFormatter
,如下所示:
extension DateFormatter {
static let standardT: DateFormatter = {
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
return dateFormatter
}()
static let standard: DateFormatter = {
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return dateFormatter
}()
static let yearMonthDay: DateFormatter = {
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter
}()
}
您现在可以通过设置 dateDecodingStrategyFormatters
直接将这些分配给解码器:
// Data structure
struct Dates: Codable {
var date1: Date
var date2: Date
var date3: Date
}
// The Json to decode
let jsonData = """
{
"date1": "2019-05-30 15:18:00",
"date2": "2019-05-30T05:18:00",
"date3": "2019-04-17"
}
""".data(using: .utf8)!
// Assigning mutliple DateFormatters
let decoder = JSONDecoder()
decoder.dateDecodingStrategyFormatters = [ DateFormatter.standardT,
DateFormatter.standard,
DateFormatter.yearMonthDay ]
do {
let dates = try decoder.decode(Dates.self, from: jsonData)
print(dates)
} catch let err as DecodingError {
print(err.localizedDescription)
}
旁注
我再次意识到将 dateDecodingStrategyFormatters
设置为 var
有点老套,我不推荐这样做,您应该定义一个函数。然而,这样做是个人偏好。
向 KeyedDecodingContainer 添加扩展
extension KeyedDecodingContainer {
func decodeDate(forKey key: KeyedDecodingContainer<K>.Key, withPossible formats: [DateFormatter]) throws -> Date? {
for format in formats {
if let date = format.date(from: try self.decode(String.self, forKey: key)) {
return date
}
}
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Date string does not match format expected by formatter.")
}
}
并使用'try container.decodeDate(forKey: 'key', withPossible: [.iso8601Full, .yyyyMMdd])'
完整的解决方案在这里:
import Foundation
extension DateFormatter {
static let iso8601Full: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.calendar = Calendar(identifier: .iso8601)
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
static let yyyyMMdd: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.calendar = Calendar(identifier: .iso8601)
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
}
public struct RSSFeed: Codable {
public let releaseDate: Date?
public let releaseDateAndTime: Date?
}
extension RSSFeed {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
releaseDate = try container.decodeDate(forKey: .releaseDate, withPossible: [.iso8601Full, .yyyyMMdd])
releaseDateAndTime = try container.decodeDate(forKey: .releaseDateAndTime, withPossible: [.iso8601Full, .yyyyMMdd])
}
}
extension KeyedDecodingContainer {
func decodeDate(forKey key: KeyedDecodingContainer<K>.Key, withPossible formats: [DateFormatter]) throws -> Date? {
for format in formats {
if let date = format.date(from: try self.decode(String.self, forKey: key)) {
return date
}
}
throw DecodingError.dataCorruptedError(forKey: key, in: self, debugDescription: "Date string does not match format expected by formatter.")
}
}
let json = """
{
"releaseDate":"2017-11-12",
"releaseDateAndTime":"2017-11-16 02:02:55"
}
"""
let data = Data(json.utf8)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601Full)
let rssFeed = try! decoder.decode(RSSFeed.self, from: data)
let feed = rssFeed
print(feed.releaseDate, feed.releaseDateAndTime)