obliczu tego samego problemu, napisałem następujące rozszerzenia:
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)")
}
})
}
}
To rozszerzenie pozwala stworzyć DateDecodingStrategy dla JSONDecoder że uchwyty wiele różnych formatów dat w obrębie tego samego ciągu JSON. Rozszerzenie zawiera funkcję, która wymaga implementacji zamknięcia, które daje kod CodingKey, i od Ciebie zależy, czy podasz poprawny DateFormatter dla dostarczonego klucza.
Powiedzmy, że masz następujące JSON:
{
"publication_date": "2017-11-02",
"opening_date": "2017-11-03",
"date_updated": "2017-11-08 17:45:14"
}
Poniższy Struct:
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"
}
}
Następnie do dekodowania JSON, należy użyć następującego kodu:
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)
Pierwsze podejście to to, którego szukałem. Dzięki! – RamwiseMatt
Z 'Codable' wydaje się dziwne, że wszystkie inne informacje mapowania json są dostarczane bezpośrednio z odpowiednich obiektów (np. Mapowanie do kluczy json za pomocą' CodingKeys'), ale formatowanie daty jest konfigurowane przez 'JSONDecoder' dla całego drzewa DTO . Po użyciu Mantle w przeszłości, ostatni z proponowanych rozwiązań wydaje się najbardziej odpowiedni, nawet jeśli oznacza to powtórzenie kodu mapowania dla innych pól, które mogłyby zostać wygenerowane automatycznie w inny sposób. – fabb