2017-10-19 7 views
1

Y at-il une possibilité d'ajouter plusieurs JSONDecoder.DateDecodingStrategy à un même JSONDecoder?Décodable multiple DateDecodingStrategies

J'ai obtenu un décodable:

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 
} 

Maintenant, j'ai eu un problème parce que l'API fournit différents formats de date. Pour pulicaion_date, opening_date, le modèle suivant est utilisé le 10 octobre 2017. Mais pour le date_updated ils envoient les données formatées comme 2017-10-10 12:21:02.

Quand je tournerai la DateDecodingStrategy du JSONDecoder à .formatted (myDateFormatter), il est écrasé pour le date_updated

Le décodeur appelé cette façon

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

et accidents:

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) 
) 
+0

Pouvez-vous poster un exemple de JSON? –

Répondre

4

Vous pouvez utiliser l'option DateDecodingStrategy.custom pour cela. Un court exemple:

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)") 
    } 
} 

J'ai créé les deux instances DateFormatteren dehors la fonction comme une simple optimisation. En tant que tel, chaque appel n'aura pas besoin de les recréer/configurer pour chaque date décodée.

Enfin, définissez la dateDecodingStrategy en utilisant la fonction que nous avons créé ci-dessus:

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)