使用 JSONDecoder 解码古怪的日期格式

Posted

技术标签:

【中文标题】使用 JSONDecoder 解码古怪的日期格式【英文标题】:Decoding oddball date format with JSONDecoder 【发布时间】:2018-09-18 07:28:06 【问题描述】:

我在 JSON 返回中获得了以前从未见过的日期格式。

"/Date(965620800000-0400)/"

(是Mon Aug 07 2000 00:00:00 GMT-0400

它是以毫秒为单位的日期值,带有 GMT 偏移量。我正在尝试使用 Swift 的原生 JSONDecoder 并设置 dateDecodingStrategy

最初,我尝试了这个:

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .millisecondsSince1970

正如我最初所怀疑的那样,由于额外的非数字字符,它不起作用。这是我得到的最初错误:

debugDescription: "预期解码 Double 但找到一个字符串/数据 相反。”,基础错误:无))无法读取数据,因为它 格式不正确。

我翻遍了Unicode.org and found this,上面写着A 是毫秒,Z 是 ISO8601 基本格式,包含小时、分钟和可选的秒字段。

考虑到这一点,我做了一个DateFormatter 扩展:

extension DateFormatter 
    static let nhtsaFormat: DateFormatter = 
        let formatter = DateFormatter()

        // This is the string that comes back --> "/Date(965620800000-0400)/"

        // These are the formats I tried:
        formatter.dateFormat = "'/Date('AZ')/'"
        // formatter.dateFormat = "'/Date('A')/'"
        // formatter.dateFormat = "'/Date('A`-`Z')/'"
        // formatter.dateFormat = "'/Date('A'-'Z')/'"
        // formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    ()

在我的DataManager 课程上,我将decoder.dateDecodingStrategy 更改为我的自定义格式,如下所示:

decoder.dateDecodingStrategy = .formatted(.nhtsaFormat)

对于每种不同的格式,我仍然收到此错误:

debugDescription: "预期解码 Double 但找到一个字符串/数据 相反。”,基础错误:无))无法读取数据,因为它 格式不正确。

我尝试从我的 Codable 结构中删除有问题的日期键,我得到了正确的回报,但不幸的是,我也需要日期。非常感谢我如何解码该字符串的任何建议。

【问题讨论】:

仅供参考 - A 是“一天中的毫秒数”。它不是“自某个时代以来的毫秒数”。没有可以处理此字符串的格式说明符。 @rmaddy 谢谢!我可以将它作为字符串引入并执行计算 var 以去除多余的字符,除以 1000,计算日期,对吗? 【参考方案1】:

您不能使用标准DateFormatter 解析这样的日期字符串,因为没有标准格式说明符表示“自纪元以来的秒数(或毫秒)”。 A 格式说明符用于“一天中的秒数”。

一种解决方案是使用.custom 日期解码策略和可以解析此类字符串的自定义方法。

下面是一些有效的测试代码:

func customDateParser(_ decoder: Decoder) throws -> Date 
    let dateString = try decoder.singleValueContainer().decode(String.self)
    let scanner = Scanner(string: dateString)
    var millis: Int64 = 0
    if scanner.scanString("/Date(", into: nil) &&
       scanner.scanInt64(&millis) &&
       scanner.scanInt(nil) &&
       scanner.scanString(")/", into: nil) &&
       scanner.isAtEnd 
        return Date(timeIntervalSince1970: TimeInterval(millis) / 1000)
     else 
        return Date() // TODO - unexpected format, throw error
    


let json = " \"date\": \"/Date(965620800000-0400)/\" ".data(using: .utf8)!

struct Test: Decodable 
    var date: Date


let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(customDateParser)
let test = try! decoder.decode(Test.self, from: json)
print(test)

请注意,日期字符串中的时区偏移量无关紧要。不需要生成正确的Date 实例。

我将错误处理留给读者作为练习。

【讨论】:

谢谢!我学到了一些很酷的东西!非常感谢。

以上是关于使用 JSONDecoder 解码古怪的日期格式的主要内容,如果未能解决你的问题,请参考以下文章

具有多种日期格式的 JSONDecoder?

使用 JSONDecoder 解码 [[String]]?

iOS - JSONEncoder和JSONDecoder介绍

Swift 的 JSONDecoder 在 JSON 字符串中有多种日期格式?

无法使用 JSONDecoder 返回或解码数据

如何使用 JSONDecoder 解码自定义 JSON 值