Ram*_*att 21 json-deserialization swift codable
Swift JSONDecoder
提供了一个dateDecodingStrategy
属性,允许我们根据DateFormatter
对象定义如何解释传入的日期字符串.
但是,我目前正在使用一个API,它返回date strings(yyyy-MM-dd
)和datetime strings(yyyy-MM-dd HH:mm:ss
),具体取决于属性.有没有办法JSONDecoder
处理这个,因为提供的DateFormatter
对象一次只能处理dateFormat
一个?
一个火腿解决方案是重写附带的Decodable
模型,只接受字符串作为其属性,并提供公共Date
getter/setter变量,但这对我来说似乎是一个糟糕的解决方案.有什么想法吗?
Les*_*rna 35
请尝试解码器配置与此类似:
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
}()
Run Code Online (Sandbox Code Playgroud)
Ita*_*ber 30
有几种方法可以解决这个问题:
DateFormatter
首先尝试日期时间字符串格式的子类,然后如果失败,则尝试普通日期格式.custom
Date
解码策略,其中您要求Decoder
a singleValueContainer()
,解码字符串,并在将解析的日期传递出去之前将其传递给您想要的任何格式化程序Date
提供了一个自定义类型init(from:)
和encode(to:)
它做到这一点(但是这是不是真的比任何一个更好的.custom
策略)init(from:)
在使用这些日期的所有类型上提供自定义,并在那里尝试不同的东西总而言之,前两种方法可能是最简单和最干净的 - 您将保持默认的合成实现,Codable
而不会牺牲类型安全性.
Oly*_*tre 17
斯威夫特 5
实际上基于使用JSONDecoder
扩展的@BrownsooHan 版本
JSONDecoder+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)")
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
添加一个只能设置的变量是一种有点hacky的方法,但是您可以var dateDecodingStrategyFormatters
通过以下方式轻松转换func setDateDecodingStrategyFormatters(_ formatters: [DateFormatter]? )
用法
假设您已经DateFormatter
在代码中定义了几个s,如下所示:
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
}()
}
Run Code Online (Sandbox Code Playgroud)
您现在可以通过设置直接将这些分配给解码器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)
}
Run Code Online (Sandbox Code Playgroud)
旁注
再次我知道,设定dateDecodingStrategyFormatters
一个var
有点哈克,而且我不推荐它,你应该定义一个函数。然而,这样做是个人偏好。
S.M*_*ore 14
面对同样的问题,我写了以下扩展名:
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)")
}
})
}
}
Run Code Online (Sandbox Code Playgroud)
此扩展允许您为JSONDecoder创建DateDecodingStrategy,以处理同一JSON字符串中的多种不同日期格式.该扩展包含一个函数,该函数需要实现一个为您提供CodingKey的闭包,并且您可以为所提供的密钥提供正确的DateFormatter.
假设您有以下JSON:
{
"publication_date": "2017-11-02",
"opening_date": "2017-11-03",
"date_updated": "2017-11-08 17:45:14"
}
Run Code Online (Sandbox Code Playgroud)
以下结构:
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"
}
}
Run Code Online (Sandbox Code Playgroud)
然后要解码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)
Run Code Online (Sandbox Code Playgroud)
Bro*_*Han 10
尝试这个。(第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
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
7545 次 |
最近记录: |