【问题标题】:Converting a String element to a complex type using Codable使用 Codable 将 String 元素转换为复杂类型
【发布时间】:2019-04-20 04:58:41
【问题描述】:

上下文

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

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

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

问题

但是,我想将 String 转换为复杂的 Swift 对象(枚举/结构)。本质上,我正在尝试根据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:错误:无法转换的值 将“字符串”键入预期的参数类型“RecurrenceSchema?” rrule:“频率=每周”, ^~~~~~~~~~~~~~~~

有什么建议吗?还是在解码之外构建解析器更好/更容易 - 并且只在编码/解码逻辑中处理字符串?

谢谢!

【问题讨论】:

  • Decodable 绝对不是解析重复规则的方法。如果你好奇,我写了一个 RRULE 解析器。见github.com/rmaddy/RWMRecurrenceRule
  • 您需要提供自己的“编码”和“解码”路径来自定义读取/写入 JSON 的方式。首先查看Ultimate Guide to JSON Parsing with Swift 4 进行检查
  • @MadProgrammer,感谢您提供指向解析指南的指针。非常有帮助。我能够根据RecurrenceSchema 来定义ScheduledEventlet rrule = try container.decode(RecurrenceSchema.self, forKey: .rrule)。然而,就目前而言,我需要使用解析器来提取 RRULE 值的每个成员。
  • @rmaddy,看起来RWMRuleParser 可以用parse(rule:)rule(from:) 来解决问题。是否无法通过 Carthage 进行安装?
  • 我不知道迦太基是什么,所以我猜不是。 :)

标签: json swift codable


【解决方案1】:

因此,在查看了不同的建议并尝试让Codable 工作之后,我实际上已经决定根据提供的重复字符串为我的重复规则使用初始化器。

字符串仍然是通过 Codable 从 JSON 中解析出来的,但这就是我要停止的地方。规则的“内部”解析是使用'String.split(separator:) 完成的。然后使用一些 map / reduce 操作将所有标记提供给 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]!)!
        [....]
    }
}

可能有更好/更简单的方法来实现这一点,但这就是我目前所拥有的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-11
    • 1970-01-01
    • 2019-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多