【问题标题】:How do I decode date JSON into Swift model?如何将日期 JSON 解码为 Swift 模型?
【发布时间】:2021-05-04 13:36:54
【问题描述】:

我需要什么样的数据模型来解码这种 json 日期格式?

示例 json:

{
    createdAt: "2021-01-30T22:48:00.469Z",
    updatedAt: "2021-01-30T22:48:00.490Z"
}

我尝试使用此模型,但不断收到解码错误...

struct Date: Decodable {
    var createdAt: Date
    var updatedAt: Date   
}

【问题讨论】:

  • 您需要自定义日期解码策略和/或自定义日期格式化程序
  • 您收到此错误是因为默认的 dateDecodingStrategy 是 deferredToDate ,它需要数量。自参考日期(UTC 时间 2001 年 1 月 1 日午夜)起的秒数
  • 我很惊讶,在您的previous questions 之一中,您写道是的,我有一个自定义日期解码策略未包含在此代码中:
  • Vadian,我已经学习 iOS 编码 (Swift) 3 个月了,所以我仍然在努力学习基础知识并弄清楚如何使代码可重用......

标签: ios json swift date decoding


【解决方案1】:

首先,不要将您的自定义类型命名为Date - 它与标准库的Date 冲突。我把它重命名为DateInfo

struct DateInfo: Decodable {
   var createdAt: Date
   var updatedAt: Date   
}

然后,要将日期解码为Date,您需要在JSONDecoder 上设置dateDecodingStrategy 以选择日期格式。在您的示例中,这是标准的 iso8601 格式,但带有小数秒,并且(感谢 @LeoDabus)内置解码策略 .iso8601 不支持:

所以,没有小数秒,它会像这样完成:

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

let dateInfo = try decoder.decode(DateInfo.self, from: jsonData)

但是对于小数秒,使用ISO8601DateFormatter 进行解码和格式化需要一些手动工作。为方便起见,我们可以使用自定义格式化程序和日期解码策略创建扩展:

extension Formatter {
   static var customISO8601DateFormatter: ISO8601DateFormatter = {
      let formatter = ISO8601DateFormatter()
      formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
      return formatter
   }()
}

extension JSONDecoder.DateDecodingStrategy {
   static var iso8601WithFractionalSeconds = custom { decoder in
      let dateStr = try decoder.singleValueContainer().decode(String.self)
      let customIsoFormatter = Formatter.customISO8601DateFormatter
      if let date = customIsoFormatter.date(from: dateStr) {
         return date
      }
      throw DecodingError.dataCorrupted(
               DecodingError.Context(codingPath: decoder.codingPath, 
                                     debugDescription: "Invalid date"))
   }
}

并且用法类似于内置策略,除了使用自定义策略:

decoder.dateDecodingStrategy = .iso8601WithFractionalSeconds

【讨论】:

  • 这不支持小数秒
  • @LeoDabus,啊,真的....该死,这很烦人,它不支持小数秒。谢谢 - 更新了答案
  • 我昨天已经发布了这个方法的链接。顺便说一句,无需将时区设置为 UTC,这已经是默认设置。你也可以写.init而不是DecodingError.Context(类型推断)并且不需要将customISO8601DateFormatter声明为var
  • 你应该提到你从Leo's answer复制&粘贴了代码
  • @vadian,嗯..它确实有相似之处,但我没有复制粘贴它。
【解决方案2】:

这与您选择的名称有关,您始终必须选择不同的名称。 您可以使用此代码访问和使用数据:

struct AboutDate: Codable {
    let createdAt, updatedAt: String?
}


extension AboutDate {
    init(data: Data) throws {
        self = try newJSONDecoder().decode(AboutDate.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func with(
        createdAt: String?? = nil,
        updatedAt: String?? = nil
    ) -> AboutDate {
        return AboutDate(
            createdAt: createdAt ?? self.createdAt,
            updatedAt: updatedAt ?? self.updatedAt
        )
    }

    func jsonData() throws -> Data {
        return try newJSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

func newJSONDecoder() -> JSONDecoder {
    let decoder = JSONDecoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        decoder.dateDecodingStrategy = .iso8601
    }
    return decoder
}

func newJSONEncoder() -> JSONEncoder {
    let encoder = JSONEncoder()
    if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
        encoder.dateEncodingStrategy = .iso8601
    }
    return encoder
}

使用方法:

do {
   let aboutDate = try AboutDate(json)
}
catch {
   //handle err
}

获取数据后,可以将字符串格式化为日期

【讨论】:

  • 这不支持小数秒
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-12
  • 2018-06-28
  • 1970-01-01
  • 2021-05-03
  • 1970-01-01
相关资源
最近更新 更多