Как JSON кодировать несколько форматов даты в одной структуре - PullRequest
0 голосов
/ 30 января 2019

Необходимо кодировать в JSON, структуру, которая имеет 2 переменные экземпляра Date (день и время), однако мне нужно кодировать каждую переменную экземпляра даты в другом формате, т.е.для "day": "yyyy-Md" и "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

1 Ответ

0 голосов
/ 30 января 2019

В пользовательском encode(to:) просто закодируйте каждый как строку, используя желаемый форматер.В JSON нет типа «дата»;это просто строкаЧто-то вроде этого:

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.

...