使用Codable

时间:2018-11-17 01:38:26

标签: json swift codable

上下文

当前正在解码包含以下元素的JSON数据:

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

我解析JSON数据并将rrule的值收集到字符串中。到目前为止,一切正常。

问题

但是,我想将String转换为复杂的Swift对象(枚举/结构)。本质上,我试图根据RFC 5545中定义的格式来解析该字符串。

是否可以使用可编码(可编码,可解码)来做到这一点?我了解它与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?'       规则:“ FREQ =每周”,              ^ ~~~~~~~~~~~~

有什么建议吗?还是在解码之外构建解析器会更好/更容易-只需在编码/解码逻辑中处理String即可?

谢谢!

1 个答案:

答案 0 :(得分:0)

因此,在查看了不同的建议并尝试解决Codable的麻烦之后,我实际上决定根据提供的重复字符串对初始化规则使用初始化程序。

仍然可以通过Codable从JSON解析字符串,但这是我停止的地方。使用'String.split(separator:)完成规则的“内部”解析。然后,使用一些map / reduce操作将所有标记馈送到Dictionary中,然后再使用Dictionary构建递归规则对象。下面的粗糙初稿:

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]!)!
        [....]
    }
}

可能有更好/更容易的方法来实现这一目标,但这就是我现在要掌握的。