假设你有一个简单的
struct Stuff // :Codable ???
{
a: String
b: String
c: String
}
var stuff: Stuff
您有如下所示的消息
msg = "b Peach"
在示例中,stuff.b 应设置为"桃子"。
(这句话很明确。所以,stuff.a 和 stuff.c 不会被改变。
朴素的代码会有一个很长的开关语句......
func processMessage
vv = msg.split
switch vv[0] {
case "a": stuff.a = vv[1]
case "b": stuff.b = vv[1]
case "c": stuff.c = vv[1]
default: print incorrect key!
}
请注意,该代码会将 stuff.b 设置为"Peach",stuff.a 和 stuff.c 不会更改。
当然,这是非常笨拙的。
当然,在Swift 中有一种更好的方法来做到这一点- 也许是用Decodable
?
--
PS 显然你可以只使用字典。不是问题的重点,谢谢。
鉴于您的草图,您似乎实际上并不想解码任何东西。看起来您想更新已经存在的东西,因此 Codable 不是正确的模型。你可能想要的是keyPaths。最直接(也是灵活(的方法是明确命名它们:
static var keyMap: [String : WritableKeyPath<Stuff, String>] {
[ "a": .a,
"b": .b,
"c": .c,
]
}
也许可以通过 Mirror 自动创建它,但我不知道有什么好方法可以做到这一点。有了这个,更新就相当简单了:
- 拆分邮件
- 选择正确的键路径
- 使用 keyPath 下标分配新值
这是代码。
extension Stuff {
static var keyMap: [String : WritableKeyPath<Stuff, String>] { ... }
struct BadMessage: Error {}
mutating func update(with message: String) throws {
let comp = message.split(separator: " ")
guard comp.count == 2, let key = Self.keyMap[String(comp[0])] else {
throw BadMessage()
}
let value = String(comp[1])
self[keyPath: key] = value
}
}
一种方法是将字符串转换为 json,如下所示
let msg = "b Peach"
let arr = msg.split(separator: " ")
let out = "{"(arr[0])": "(arr[1])"}"
out
现在是 {"b": "桃子"}
然后你可以像普通的json解码一样处理它
struct Stuff: Decodable {
let a: String?
let b: String?
let c: String?
}
let data = out.data(using: .utf8)!
do {
let result = try JSONDecoder().decode(Stuff.self, from: data)
} catch {
error
}
我的意思是这样:
struct Stuff {
let kind: Kind
let value: String
init(from text: String) throws {
let components = text.components(separatedBy: " ")
guard let rawKind = components.first else { throw Error.noKind }
guard let kind = Kind(rawValue: rawKind) else { throw Error.unknownKind(rawKind) }
self.kind = kind
value = components.dropFirst().joined(separator: " ")
}
enum Kind: String { case a, b, c }
enum Error: Swift.Error {
case noKind
case unknownKind(String)
}
}
如果需要,初始化器可以很容易地替换为解码器初始值设定项,如下所示:
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let text = try container.decode(String.self)
try self.init(from: text)
}
但请注意,您不能直接从String
解码Stuff.self
,因为它不是 json。 但您可以在包装器 JSON 中使用它。