用于简单键/值对的自定义解码器(或 ?)



假设你有一个简单的

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 中使用它。

相关内容

  • 没有找到相关文章

最新更新