[英]Swift 4 Codable: Converting JSON return String to Int/Date/Float
我正在經歷一些項目並刪除 JSON 解析框架,因為使用 Swift 4 似乎很簡單。我遇到了這個奇怪的Ints
返回的Dates
和返回的Strings
。
我查看了GrokSwift 的 Parsing JSON 和 Swift 4 , Apple 的網站,但我沒有看到任何跳出來的東西:改變類型。
Apple 的示例代碼顯示了如何更改鍵名,但我很難弄清楚如何更改鍵類型。
這是它的樣子:
{
"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"
}
}
}
這仍然給我留下了應該是Int
或Date
的String
。 我將如何 go 關於使用 Codable 協議將包含Int/Date/Float
作為String
的 JSON 返回轉換為Int/Date/Float
?
這還不可能,因為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"
}
}
}
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)
讓我提出兩種方法:一種用於處理String
支持的值,另一種用於處理可能以不同格式出現的日期。 希望這個例子是不言自明的。
import Foundation
protocol StringRepresentable: CustomStringConvertible {
init?(_ string: String)
}
extension Int: StringRepresentable {}
extension Double: StringRepresentable {}
struct StringBacked<Value: StringRepresentable>: Codable, CustomStringConvertible {
var value: Value
var description: String {
value.description
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
guard let value = Value(string) else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: """
Failed to convert an instance of \(Value.self) from "\(string)"
"""
)
}
self.value = value
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(value.description)
}
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in
let container = try decoder.singleValueContainer()
let dateStr = try container.decode(String.self)
let formatters = [
"yyyy-MM-dd",
"yyyy-MM-dd'T'HH:mm:ssZZZZZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ",
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
"yyyy-MM-dd HH:mm:ss",
"MM/dd/yyyy HH:mm:ss",
"MM/dd/yyyy hh:mm:ss a"
].map { (format: String) -> DateFormatter in
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = format
return formatter
}
for formatter in formatters {
if let date = formatter.date(from: dateStr) {
return date
}
}
throw DecodingError.valueNotFound(String.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not parse json key: \(container.codingPath), value: \(dateStr) into a Date"))
})
// Test it with data:
let jsonData = """
{
"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"
}
]
}
""".data(using: .utf8)!
struct WaitTimeContainer: Codable {
let waitTimes: [WaitTime]
private enum CodingKeys: String, CodingKey {
case waitTimes = "WaitTimes"
}
struct WaitTime: Codable {
var checkpointIndex: Int {
get { return checkpointIndexString.value }
set { checkpointIndexString.value = newValue }
}
var waitTime: Double {
get { return waitTimeString.value }
set { waitTimeString.value = newValue }
}
let createdDateTime: Date
private var checkpointIndexString: StringBacked<Int>
private var waitTimeString: StringBacked<Double>
private enum CodingKeys: String, CodingKey {
case checkpointIndexString = "CheckpointIndex"
case waitTimeString = "WaitTime"
case createdDateTime = "Created_Datetime"
}
}
}
let waitTimeContainer = try decoder.decode(WaitTimeContainer.self, from: jsonData)
print(waitTimeContainer)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.