Swift 4 Codable:将 JSON 返回字符串转换为 Int/Date/Float
Posted
技术标签:
【中文标题】Swift 4 Codable:将 JSON 返回字符串转换为 Int/Date/Float【英文标题】:Swift 4 Codable: Converting JSON return String to Int/Date/Float 【发布时间】:2018-04-04 17:09:37 【问题描述】:我正在经历一些项目并删除 JSON 解析框架,因为使用 Swift 4 似乎很简单。我遇到了这个奇怪的 JSON 返回,其中 Ints
和 Dates
返回为 Strings
。
我查看了GrokSwift's Parsing JSON with Swift 4、Apple's website,但没有看到任何跳出的内容:改变类型。
Apple's example code 展示了如何更改键名,但我很难弄清楚如何更改键类型。
它是这样的:
"WaitTimes": [
"CheckpointIndex": "1",
"WaitTime": "1",
"Created_Datetime": "10/17/2017 6:57:29 PM"
,
"CheckpointIndex": "2",
"WaitTime": "6",
"Created_Datetime": "10/12/2017 12:28:47 PM"
,
"CheckpointIndex": "0",
"WaitTime": "8",
"Created_Datetime": "9/26/2017 5:04:42 AM"
]
我使用CodingKey
将字典键重命名为符合 Swift 的条目,如下所示:
struct WaitTimeContainer: Codable
let waitTimes: [WaitTime]
private enum CodingKeys: String, CodingKey
case waitTimes = "WaitTimes"
struct WaitTime: Codable
let checkpointIndex: String
let waitTime: String
let createdDateTime: String
private enum CodingKeys: String, CodingKey
case checkpointIndex = "CheckpointIndex"
case waitTime = "WaitTime"
case createdDateTime = "Created_Datetime"
这仍然给我留下了String
,应该是Int
或Date
。如何使用 Codable 协议将包含 Int/Date/Float
作为 String
的 JSON 返回转换为 Int/Date/Float
?
【问题讨论】:
@Adrian 确保 Created_Datetime 存储到服务器时是 UTC 时间而不是本地时间,否则在解析日期时不应将日期格式化程序时区设置为零 secondsFromGMT。 【参考方案1】:public extension KeyedDecodingContainer
public func decode(_ type: Date.Type, forKey key: Key) throws -> Date
let dateString = try self.decode(String.self, forKey: key)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yyyy hh:mm:ss a"
guard let date = dateFormatter.date(from: dateString) else
let context = DecodingError.Context(codingPath: codingPath,
debugDescription: "Could not parse json key to a Date")
throw DecodingError.dataCorrupted(context)
return date
用法:-
let date: Date = try container.decode(Date.self, forKey: . createdDateTime)
【讨论】:
请注意:您应该扩展 Formatter 并从您的方法中创建一个静态 DateFormatter。此外,您需要将其语言环境设置为“en_US_POSIX”,否则它可能会由于用户设备时间格式设置为 24 小时而不是 12 小时而失败。 如果您真的想在您的方法中声明您的日期格式化程序,您还应该提供另一个参数,允许开发人员传递自定义 dateFormat。顺便说一句,您还可以将名称更改为 decodeDate 并删除第一个参数类型:不需要的 Date.Type。 您还可以使用字符串插值在 debugDescription 中包含解码失败的键和值。extension Formatter static let custom: DateFormatter = let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "MM/dd/yyyy hh:mm:ss a" return formatter ()
extension KeyedDecodingContainer func decodeCustomDate(forKey key: Key) throws -> Date let string = try decode(String.self, forKey: key) guard let date = Formatter.custom.date(from: string) else let context = DecodingError.Context( codingPath: codingPath, debugDescription: "Could not parse json key: \(key), value: \(string) into a Date" ) throw DecodingError.dataCorrupted(context) return date
【参考方案2】:
这是not yet possible,因为 Swift 团队在 JSONDecoder 中只提供了 String 到日期解码器。
您始终可以手动解码:
struct WaitTimeContainer: Decodable
let waitTimes: [WaitTime]
private enum CodingKeys: String, CodingKey
case waitTimes = "WaitTimes"
struct WaitTime:Decodable
let checkpointIndex: Int
let waitTime: Float
let createdDateTime: Date
init(checkpointIndex: Int, waitTime: Float, createdDateTime:Date)
self.checkpointIndex = checkpointIndex
self.waitTime = waitTime
self.createdDateTime = createdDateTime
static let formatter: DateFormatter =
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "MM/dd/yyyy hh:mm:ss a"
return formatter
()
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
let checkpointIndexString = try container.decode(String.self, forKey: .checkpointIndex)
let checkpointIndex = Int(checkpointIndexString)!
let waitTimeString = try container.decode(String.self, forKey: .waitTime)
let waitTime = Float(waitTimeString)!
let createdDateTimeString = try container.decode(String.self, forKey: .createdDateTime)
let createdDateTime = WaitTime.formatter.date(from: createdDateTimeString)!
self.init(checkpointIndex:checkpointIndex, waitTime:waitTime, createdDateTime:createdDateTime)
private enum CodingKeys: String, CodingKey
case checkpointIndex = "CheckpointIndex"
case waitTime = "WaitTime"
case createdDateTime = "Created_Datetime"
【讨论】:
太棒了!这样就完成了工作!我将保留它以供将来草率的 JSON 返回时参考。谢谢! @Adrian:很高兴能帮到你。 超级有帮助。希望苹果在发布之前就想到了这些事情。基本上需要同样多的解析工作,如果不是更多的话。谢谢!以上是关于Swift 4 Codable:将 JSON 返回字符串转换为 Int/Date/Float的主要内容,如果未能解决你的问题,请参考以下文章
Swift Codable:如何将***数据编码到嵌套容器中
如何使用 Swift 创建 JSON Codable [关闭]
使用 Codable 序列化为 JSON 时的 Swift 字符串转义