假设您有一个API响应模型的struct。假设它有50个成员。然而,5-7个成员是非标准的套管,你可以有AUsernAme或_BTmember,但其余都是蛇案例credit_score或status_code。
而不是像这样写所有的成员:
struct MyStruct {
let aUserName: String
// +50 more...
private enum CodingKeys: String, CodingKey {
case aUserName = "AUsernAme"
// +50 more...
}
}我们有办法写成这样吗?
struct MyStruct {
@CodingKey("AUsernAme") let aUserName: String
let creditScore: Int
// +50 more ...
}编辑:--我想现在的Swift版本是不可能的,但是有人知道这是否会被包含在Swift的未来版本中吗?
发布于 2022-06-26 16:00:01
Sweeper提供的解决方案是解决您的问题的一个很好的解决方案,但IMO可能会对您的问题和将要阅读此代码的下一个开发人员显示出极大的复杂性。
如果我是你,为了简单起见,我只会坚持编写所有的CodingKeys。如果您的担心是编写了大量的案例,那么您可以在一行中编写所有不需要自定义键的情况,只需在新行中添加带有异常/非标准大小写的键:
case property1, property2, property3, property4, property5...
case property50 = "_property50"既然你提到剩下的都是蛇箱,还不确定你是否知道,但我们有JSONDecoder.KeyDecodingStrategy.convertFromSnakeCase。
希望这能帮助` `tol!:)
发布于 2022-06-26 11:10:23
不如在解码之前设置一个自定义keyDecodingStrategy如何?
struct AnyCodingKey: CodingKey, Hashable {
var stringValue: String
init(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init(intValue: Int) {
self.intValue = intValue
self.stringValue = "\(intValue)"
}
}
let mapping = [
"AUsernAme": "aUserName",
// other mappings...
]
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom({ codingPath in
let key = codingPath[0].stringValue
guard let mapped = mapping[key] else { return codingPath.last! }
return AnyCodingKey(stringValue: mapped)
})这假设您的JSON有一个单一的平面结构。您可以将其转换为一个扩展:
extension JSONDecoder.KeyDecodingStrategy {
static func mappingRootKeys(_ dict: [String: String]) -> JSONDecoder.KeyDecodingStrategy {
return .custom { codingPath in
let key = codingPath[0].stringValue
guard let mapped = dict[key] else { return codingPath.last! }
return AnyCodingKey(stringValue: mapped)
}
}
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .mappingRootKeys(mapping)如果JSON有更多级别,则可以将字典的类型更改为[JSONPath: String],其中JSONPath是可以在嵌套JSON中创建的表示键的类型。然后添加一些代码,将编码路径(这只是一个编码键数组)转换为JSONPath。这应该不难自己写。
一个简单的方法是只使用[AnyCodingKey]作为JSONPath,但是还有很多其他的方法,我鼓励您尝试并找到您最喜欢的方法。
typealias JSONPath = [AnyCodingKey]
extension AnyCodingKey {
init(codingKey: CodingKey) {
if let int = codingKey.intValue {
self.init(intValue: int)
} else {
self.init(stringValue: codingKey.stringValue)
}
}
}
extension JSONDecoder.KeyDecodingStrategy {
static func mappingRootKeys(_ dict: [JSONPath: String]) -> JSONDecoder.KeyDecodingStrategy {
return .custom { codingPath in
guard let mapped = dict[codingPath.map(AnyCodingKey.init(codingKey:))] else { return codingPath.last! }
return AnyCodingKey(stringValue: mapped)
}
}
}
let mapping = [
[AnyCodingKey(stringValue: "AUsernAme")]: "aUserName"
]不可能为此使用属性包装器。您的属性包装器@CodingKey("AUsernAme") let aUserName: String将被编译成如下所示(按照这里):
private var _aUserName: CodingKey<String> = CodingKey("AUsernAme")
var aUserName: String {
get { _aUserName.wrappedValue }
set { _aUserName.wrappedValue = newValue }
}这方面有两个主要问题:
init(from:)中的所有50+属性编写MyStruct,代码将被合成来解码,并分配给它的_aUserName属性。您只能控制CodingKey属性包装器的CodingKey初始化器,并且不能在其中对MyStruct进行任何解码。如果MyStruct包含在另一个结构中:
结构AnotherStruct:可解码{ let myStruct: MyStruct }
然后,您确实可以通过使用属性包装器对myStruct进行标记来控制用于解码它的编码键。在解码过程中,您可以通过实现属性包装器的init(from:)来做您想做的任何事情,这就引出了第二个问题:CodingKey属性包装器的编码键是通过表单init(_ key: String)的初始化项传递的。但是您可以通过初始化器init(from decoder: Decoder)来控制解码,因为这就是在对结构进行解码时将调用的内容。换句话说,您无法将密钥映射发送到属性包装器。https://stackoverflow.com/questions/72760627
复制相似问题