Swift 4 Codable: преобразование возвращаемой строки JSON в Int / Date / Float

4

Я просматриваю некоторые проекты и удаляю рамки JSON для синтаксического анализа, так как это кажется довольно простым для Swift 4. Я столкнулся с этим возвратом JSON с нечетным кодом, где Ints и Dates возвращаются как Strings.

Я посмотрел на GrokSwift Parsing JSON с Swift 4, Apple сайт, но я не вижу ничего, что выпрыгивает из: изменение типов.

Пример кода Apple показывает, как изменять имена ключей, но мне сложно определить, как изменить тип ключа.

Вот как это выглядит:

{
    "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, который должен быть Int или Date. Как мне перейти на преобразование возврата JSON, содержащего Int/Date/Float как String в Int/Date/Float, используя протокол Codable?

  • 1
    @Adrian убедитесь, что Created_Datetime при сохранении на сервере указано по времени UTC, а не по местному времени, в противном случае вам не следует устанавливать часовой пояс модуля форматирования даты равным нулю секундамFromGMT при анализе ваших дат.
Теги:
swift4
codable

2 ответа

6
Лучший ответ

Это пока не возможно, так как команда Swift предоставила только декодер String to date в JSONDecoder.

Вы всегда можете декодировать вручную:

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"
        }
    }
}
  • 1
    Потрясающие! Это сделало работу! Я сохраню это для справки о будущих небрежных возвращениях JSON. Спасибо!
  • 0
    @Adrian:I я рад, что смог вам помочь.
Показать ещё 1 комментарий
0
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)
  • 1
    Несколько замечаний: вы должны расширить Formatter и создать статический DateFormatter из вашего метода. Также вам нужно установить его локаль на «en_US_POSIX», иначе он может потерпеть неудачу из-за формата времени пользовательского устройства, установленного на 24 часа вместо 12.
  • 0
    Если вы действительно хотите объявить свой форматер даты в своем методе, вы должны также предоставить другой параметр, позволяющий разработчику передавать пользовательский формат даты. Кстати, вы также можете изменить имя на decodeDate и удалить первый тип параметра: Date.Type, который не нужен.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню