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 返回,其中 IntsDates 返回为 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,应该是IntDate。如何使用 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协议进行json转模型

Swift Codable:如何将***数据编码到嵌套容器中

如何使用 Swift 创建 JSON Codable [关闭]

使用 Codable 序列化为 JSON 时的 Swift 字符串转义

使用 swift Codable 从 JSON 数组中提取数据

如何使用 Codable 在 Swift 中使用动态文件名解析 JSON