Преобразование элемента String в сложный тип с использованием Codable - PullRequest
0 голосов
/ 17 ноября 2018

Контекст

В настоящее время декодирует данные JSON, которые содержат следующий элемент:

{
    "events": [
        {
            ...
            "rrule": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
            ...
         }
     ]
}

Я анализирую данные JSON и собираю значение rrule в строку. Пока все хорошо, все отлично работает.

Проблема

Однако я хотел бы преобразовать этот String в сложный объект Swift (enum / struct). По сути, я пытаюсь разобрать эту строку в соответствии с ее форматом, определенным в RFC 5545 .

Возможно ли сделать это с помощью Codable (Encodable, Decodable)? Я понимаю, что это несколько ортогонально к разбору данных JSON ...

Предварительное решение (ошибка)

Упрощенная версия (ограниченная декодированием FREQ) всей структуры будет выглядеть так:

public struct ScheduledEventList: Codable {
    public let events: [ScheduledEvent]
}

public struct ScheduledEvent: Codable {
    public let title: String
    public let rrule: RecurrenceSchema? = nil

    public init(
        title: String,
        rrule: RecurrenceSchema?,    <---- problem
        ) {
        self.title = title
        self.rrule = rrule
    }
}

public enum RecurrenceFrequency: String, Codable {
    case DAILY
    case WEEKLY
    case MONTHLY
    case YEARLY
}

public enum RecurrenceSchema: Codable {

    case FREQ(RecurrenceFrequency)

    private enum CodingKeys: String, CodingKey {
        case FREQ
    }

    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let value = try values.decodeIfPresent(String.self, forKey: .FREQ)
        self = .FREQ(RecurrenceFrequency(rawValue: value!)!)
    }

    public func encode(to encoder: Encoder) throws {

        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .FREQ(let freq):
            try container.encode(freq.rawValue, forKey: .FREQ)
        }
    }
}

Проблема в том, что элемент, возвращаемый из данных JSON, является (конечно) String, поэтому он жалуется на это:

ошибка: Playground.playground: 99: 12: ошибка: невозможно преобразовать значение введите String для ожидаемого аргумента типа RecurrenceSchema? rule: "FREQ = WEEKLY", ^ ~~~~~~~~~~~~

Есть предложения? Или лучше / проще построить анализатор вне декодирования - и просто иметь дело со String в логике кодирования / декодирования?

спасибо!

1 Ответ

0 голосов
/ 11 декабря 2018

Поэтому, посмотрев на различные предложения и выбрав свой собственный способ попытаться заставить Codable работать, я фактически остановился на использовании инициализатора для моего правила повторения, основанного на предоставленной строке повторения.

Строка все еще анализируется из JSON через Codable, но на этом я и остановлюсь.«Внутренний» синтаксический анализ правила выполняется с помощью 'String.split(separator:).Затем некоторые операции преобразования / уменьшения используются для подачи всех токенов в словарь, который затем используется для создания объекта правила повторения.Раннее черновое черновик ниже:

public struct RecurrenceRule {
    public let frequency: RecurrenceFrequency
    public let durationUntil: Date?
    public let durationCount: Int?
    public let interval: Int?
    public let byDay: [RecurrenceByDay]?
    public let byMonthDay: [RecurrenceByMonthDay]?
    public let byYearDay: [RecurrenceByYearDay]?
    public let byWeekNo: [RecurrenceByWeekNo]?
    public let byMonth: [Int]?
    public let bySetPos: [RecurrenceByYearDay]?

    public init(rule: String) {
        let splitResult = rule
            .split(separator: RecurrenceRuleSeparation.semicolon.rawValue)
            .map { $0.split(separator: RecurrenceRuleSeparation.equal.rawValue)}
            .map { (RecurrenceKeyword(rawValue: String($0.first!)), $0.last!) }
            .reduce([RecurrenceKeyword: String]()) { result, interm in
                var resultCopy = result
                resultCopy[interm.0!] = String(interm.1)
                return resultCopy
            }
        self.frequency = RecurrenceFrequency(rawValue: splitResult[RecurrenceKeyword.FREQ]!)!
        [....]
    }
}

Возможно, есть лучшие / более простые способы добиться этого, но это то, что у меня есть на данный момент.

...