如何 JSON 在同一结构中编码多种日期格式
How to JSON encode multiple date formats within the same struct
需要编码为 JSON,一个具有 2 个日期实例变量(日期和时间)的结构,但是,我需要使用不同的格式对每个日期实例变量进行编码,即。对于 "day":"yyyy-M-d" 和 "time":"H:m:s".
已经编写了一个没有问题的自定义解码器。但不确定如何编写所需的自定义编码器来解决这个问题。
例如,我可以解码以下 JSON 字符串:
{ "biometrics" : [
{"biometricId":1,"amount":2.1,"source":"Alderaan","day":"2019-1-3","time":" 11-3-3","unitId":2},
{"biometricId":10,"amount":3.1,"source":"Endoor","day":"2019-2-4","time":" 11-4-4","unitId":20}]
}
但是,当我对它进行编码时,我只能将其编码为单一日期格式:(
帮助,将不胜感激。
谢谢。
import UIKit
let biometricsJson = """
{ "biometrics" : [
{"biometricId":1,"amount":2.1,"source":"Alderaan","day":"2019-1-3","time":"11-3-3","unitId":2},
{"biometricId":10,"amount":3.1,"source":"Endoor","day":"2019-2-4","time":"11-4-4","unitId":20}]
}
"""
struct Biometrics: Codable {
var biometrics: [Biometric]
}
struct Biometric: Codable {
var biometricId: Int
var unitId: Int
var source: String?
var amount: Double
var day: Date
var time: Date
init(biometricId: Int, unitId: Int, source: String, amount: Double, day: Date, time: Date){
self.biometricId = biometricId
self.unitId = unitId
self.source = source
self.amount = amount
self.day = day
self.time = time
}
}
extension Biometric {
static let decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "H:m:s"
if let date = formatter.date(from: dateString) {
return date
}
formatter.dateFormat = "yyyy-M-d"
if let date = formatter.date(from: dateString) {
return date
}
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Cannot decode date string \(dateString)")
}
return decoder
}()
}
let biometrics = try Biometric.decoder.decode(Biometrics.self, from:biometricsJson.data(using: .utf8)!)
let jsonEncoder = JSONEncoder()
let encodedJson = try jsonEncoder.encode(biometrics)
let jsonString = String(data: encodedJson, encoding: .utf8)
if biometricsJson != jsonString {
print("error: decoding, then encoding does not give the same string")
print("biometricsJson: \(biometricsJson)")
print("jsonString: \(jsonString!)")
}
我希望编码后的 JSON 能够被解码器解码。
即 biometricsJson == jsonString
在自定义 encode(to:)
中,只需使用所需的格式化程序将每个编码为字符串。 JSON 中没有 "date" 类型;它只是一个字符串。沿着这些线的东西:
enum CodingKeys: CodingKey {
case biometricId, amount, source, day, time, unitId
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(biometricId, forKey: .biometricId)
try container.encode(unitId, forKey: .unitId)
try container.encode(source, forKey: .source)
try container.encode(amount, forKey: .amount)
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "H:m:s"
let timeString = formatter.string(from: time)
try container.encode(timeString, forKey: .time)
formatter.dateFormat = "yyyy-M-d"
let dayString = formatter.string(from: day)
try container.encode(dayString, forKey: .day)
}
但请注意,您无法测试等效字符串。 JSON 词典不保留顺序,因此无法保证逐个字符匹配。
请注意,如果您真的想要日期和时间,您应该考虑 DateComponents
而不是 Date
。日期是时间的特定实例;它不在任何时区,而且不能只是一小时、一分钟和一秒。
此外,您对 Double 的使用会导致舍入差异。所以 2.1 将被编码为 2.1000000000000001。如果这是一个问题,您应该使用 Decimal
作为 amount
而不是 Double
.
需要编码为 JSON,一个具有 2 个日期实例变量(日期和时间)的结构,但是,我需要使用不同的格式对每个日期实例变量进行编码,即。对于 "day":"yyyy-M-d" 和 "time":"H:m:s".
已经编写了一个没有问题的自定义解码器。但不确定如何编写所需的自定义编码器来解决这个问题。
例如,我可以解码以下 JSON 字符串: { "biometrics" : [ {"biometricId":1,"amount":2.1,"source":"Alderaan","day":"2019-1-3","time":" 11-3-3","unitId":2}, {"biometricId":10,"amount":3.1,"source":"Endoor","day":"2019-2-4","time":" 11-4-4","unitId":20}] }
但是,当我对它进行编码时,我只能将其编码为单一日期格式:( 帮助,将不胜感激。 谢谢。
import UIKit
let biometricsJson = """
{ "biometrics" : [
{"biometricId":1,"amount":2.1,"source":"Alderaan","day":"2019-1-3","time":"11-3-3","unitId":2},
{"biometricId":10,"amount":3.1,"source":"Endoor","day":"2019-2-4","time":"11-4-4","unitId":20}]
}
"""
struct Biometrics: Codable {
var biometrics: [Biometric]
}
struct Biometric: Codable {
var biometricId: Int
var unitId: Int
var source: String?
var amount: Double
var day: Date
var time: Date
init(biometricId: Int, unitId: Int, source: String, amount: Double, day: Date, time: Date){
self.biometricId = biometricId
self.unitId = unitId
self.source = source
self.amount = amount
self.day = day
self.time = time
}
}
extension Biometric {
static let decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "H:m:s"
if let date = formatter.date(from: dateString) {
return date
}
formatter.dateFormat = "yyyy-M-d"
if let date = formatter.date(from: dateString) {
return date
}
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Cannot decode date string \(dateString)")
}
return decoder
}()
}
let biometrics = try Biometric.decoder.decode(Biometrics.self, from:biometricsJson.data(using: .utf8)!)
let jsonEncoder = JSONEncoder()
let encodedJson = try jsonEncoder.encode(biometrics)
let jsonString = String(data: encodedJson, encoding: .utf8)
if biometricsJson != jsonString {
print("error: decoding, then encoding does not give the same string")
print("biometricsJson: \(biometricsJson)")
print("jsonString: \(jsonString!)")
}
我希望编码后的 JSON 能够被解码器解码。 即 biometricsJson == jsonString
在自定义 encode(to:)
中,只需使用所需的格式化程序将每个编码为字符串。 JSON 中没有 "date" 类型;它只是一个字符串。沿着这些线的东西:
enum CodingKeys: CodingKey {
case biometricId, amount, source, day, time, unitId
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(biometricId, forKey: .biometricId)
try container.encode(unitId, forKey: .unitId)
try container.encode(source, forKey: .source)
try container.encode(amount, forKey: .amount)
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "H:m:s"
let timeString = formatter.string(from: time)
try container.encode(timeString, forKey: .time)
formatter.dateFormat = "yyyy-M-d"
let dayString = formatter.string(from: day)
try container.encode(dayString, forKey: .day)
}
但请注意,您无法测试等效字符串。 JSON 词典不保留顺序,因此无法保证逐个字符匹配。
请注意,如果您真的想要日期和时间,您应该考虑 DateComponents
而不是 Date
。日期是时间的特定实例;它不在任何时区,而且不能只是一小时、一分钟和一秒。
此外,您对 Double 的使用会导致舍入差异。所以 2.1 将被编码为 2.1000000000000001。如果这是一个问题,您应该使用 Decimal
作为 amount
而不是 Double
.