可解码的多个DateDecodingStrategies

Oli*_*ver 3 json ios swift swift4 codable

是否有可能将多个添加JSONDecoder.DateDecodingStrategy到同一个JSONDecoder

我有一个可解码的:

struct Movie: Decodable {
    enum CodingKeys: String, CodingKey {
        case title = "display_title"
        case mpaaRating = "mpaa_rating"
        case criticsPick = "critics_pick"
        case byline
        case headline
        case summaryShort = "summary_short"
        case publicationDate = "publication_date"
        case openingDate = "opening_date"
        case dateUpdated = "date_updated"
        case link
        case multimedia
    }

let title: String
let mpaaRating: String
let criticsPick: Int
let byline: String
let headline: String
let summaryShort: String
let publicationDate: Date
let openingDate: Date?
let updatedDate: Date
let link: MovieLink
let multimedia: MovieMultimedia
var image: UIImage?


init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    try title = values.decode(String.self, forKey: .title)
    try mpaaRating = values.decode(String.self, forKey: .mpaaRating)
    try criticsPick = values.decode(Int.self, forKey: .criticsPick)
    try byline = values.decode(String.self, forKey: .byline)
    try headline = values.decode(String.self, forKey: .headline)
    try summaryShort = values.decode(String.self, forKey: .summaryShort)
    try openingDate = values.decodeIfPresent(Date.self, forKey: .openingDate)
    try publicationDate = values.decode(Date.self, forKey: .publicationDate)
    try updatedDate = values.decode(Date.self, forKey: .dateUpdated)
    try link = values.decode(MovieLink.self, forKey: .link)
    try multimedia = values.decode(MovieMultimedia.self, forKey: .multimedia)
}

mutating func loadImage(completion: @escaping (UIImage?, Error?) -> ()) {
    URLSession.shared.dataTask(with: self.multimedia.src) { data, _, error in
        DispatchQueue.main.async {
            if let data = data {
                let image = UIImage(data: data)
                completion(image, error)
            }
        }
        }.resume()
    }
}

struct MovieLink: Decodable {
    enum CodingKeys: String, CodingKey {
        case type
        case url
        case suggestedLinkText = "suggested_link_text"
    }

    let type: String
    let url: URL
    let suggestedLinkText: String
}

struct MovieMultimedia: Decodable {
    let type: String
    let src: URL
    let height: Int
    let width: Int
}
Run Code Online (Sandbox Code Playgroud)

现在我遇到了一个问题,因为API提供了不同的日期格式.对于pulicaion_date,opening_date使用以下模式2017-10-10.

但是对于date_updated,他们发送格式为2017-10-10 12:21:02的数据.

当我设置DateDecodingStrategyJSONDecoder,以.formatted(myDateFormatter)坠毁的日期date_updated

解码器以这种方式调用

let decoder = JSONDecoder()
let dateformatter = DateFormatter()
dateformatter.dateFormat = "yyyy-MM-dd"
decoder.dateDecodingStrategy = .formatted(dateformatter)
let movieList = try! decoder.decode(MovieList.self, from: data!)
Run Code Online (Sandbox Code Playgroud)

崩溃:

fatal error: 'try!' expression unexpectedly raised an error:
Swift.DecodingError.dataCorrupted(
Swift.DecodingError.Context(
  codingPath: [
    Test_App.MovieList.CodingKeys.movies, 
    Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0)),
    Test_App.Movie.CodingKeys.dateUpdated
  ], 
  debugDescription: "Date string does not match format expected by formatter.", 
  underlyingError: nil)
)
Run Code Online (Sandbox Code Playgroud)

Pau*_*tos 6

您可以使用该DateDecodingStrategy.custom选项.一个简短的例子:

let shortFormatter = DateFormatter()
let longFormatter = DateFormatter()
shortFormatter.dateFormat = "yyyy-MM-dd"
longFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"

func customDateFormatter(_ decoder: Decoder) throws -> Date {
    let dateString = try decoder.singleValueContainer().decode(String.self)
    let dateKey = decoder.codingPath.last as! Movie.CodingKeys
    switch dateKey {
    case .shortDate :
        return shortFormatter.date(from: dateString)!
    case .longDate :
        return longFormatter.date(from: dateString)!
    default:
        fatalError("Unexpected date coding key: \(dateKey)")
    }
}
Run Code Online (Sandbox Code Playgroud)

我在函数创建了两个DateFormatter实例,仅作为优化.因此,每个调用都不需要为每个解码日期重新创建/配置它们.

最后,dateDecodingStrategy使用我们在上面创建的函数设置:

let json =
"""
{
    "name": "A Clockwork Orange",
    "short_date": "2017-10-10",
    "long_date": "2017-10-10 12:21:02"
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(customDateFormatter)
let movie = try! decoder.decode(Movie.self, from: json)
Run Code Online (Sandbox Code Playgroud)