问题描述
我是Swift的新手,我需要使用一些 可配置密钥 解析JSON。
与我在这里看到的许多示例相反,在开始解码操作之前,已知密钥,它们仅取决于传递给端点的某些参数。
I'm new to Swift and I need to parse a JSON with some configurable keys.Opposite to many examples I've seen here, the keys are known before the decode operation is started, they just depend on some parameters passed to endpoint.
示例:
https://some.provider.com/endpoint/?param=XXX
和
https://some.provider.com/endpoint/?param=YYY
会回答分别为:
[
{
"fixed_key1": "value1",
"fixed_key2": "value2",
"variable_key_1_XXX": "some value",
"variable_key_2_XXX": "some other value"
},
...
]
和
[
{
"fixed_key1": "value1",
"fixed_key2": "value2",
"variable_key_1_YYY": "some value",
"variable_key_2_YYY": "some other value"
},
...
]
鉴于这些键在解码之前是已知的,所以我希望能够摆脱一些聪明的想法声明可解码结构和/或 CodingKeys ,而无需编写
Given that those keys are known before decoding, I was hoping to get away with some clever declaration of a Decodable structure and/or CodingKeys, without the need to write the
init(from decoder: Decoder)
不幸的是,我无法提出这样的声明。
Unfortunately, I was not able to come up with such a declaration.
当然,我不想为每个可能的参数值编写一个Decodable / CodingKeys结构:-)
Of course I don't want to write one Decodable/CodingKeys structure for every possible parameter value :-)
有什么建议吗?
推荐答案
除非您所有的JSON键都是编译时常量,否则编译器不能综合解码方法。但是,您可以做一些事情来减少手动解码的麻烦。
Unless all your JSON keys are compile-time constants, the compiler can't synthesize the decoding methods. But there are a few things you can do to make manual decoding a lot less cumbersome.
首先,一些辅助结构和扩展:
First, some helper structs and extensions:
/*
Allow us to initialize a `CodingUserInfoKey` with a `String` so that we can write:
decoder.userInfo = ["param": "XXX"]
Instead of:
decoder.userInfo = [CodingUserInfoKey(rawValue:"param")!: "XXX"]
*/
extension CodingUserInfoKey: ExpressibleByStringLiteral {
public typealias StringLiteralType = String
public init(stringLiteral value: StringLiteralType) {
self.rawValue = value
}
}
/*
This struct is a plain-vanilla implementation of the `CodingKey` protocol. Adding
`ExpressibleByStringLiteral` allows us to initialize a new instance of
`GenericCodingKeys` with a `String` literal, for example:
try container.decode(String.self, forKey: "fixed_key1")
Instead of:
try container.decode(String.self, forKey: GenericCodingKeys(stringValue: "fixed_key1")!)
*/
struct GenericCodingKeys: CodingKey, ExpressibleByStringLiteral {
// MARK: CodingKey
var stringValue: String
var intValue: Int?
init?(stringValue: String) { self.stringValue = stringValue }
init?(intValue: Int) { return nil }
// MARK: ExpressibleByStringLiteral
typealias StringLiteralType = String
init(stringLiteral: StringLiteralType) { self.stringValue = stringLiteral }
}
然后进行手动解码:
struct MyDataModel: Decodable {
var fixedKey1: String
var fixedKey2: String
var variableKey1: String
var variableKey2: String
enum DecodingError: Error {
case missingParamKey
case unrecognizedParamValue(String)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: GenericCodingKeys.self)
// Decode the fixed keys
self.fixedKey1 = try container.decode(String.self, forKey: "fixed_key1")
self.fixedKey2 = try container.decode(String.self, forKey: "fixed_key2")
// Now decode the variable keys
guard let paramValue = decoder.userInfo["param"] as? String else {
throw DecodingError.missingParamKey
}
switch paramValue {
case "XXX":
self.variableKey1 = try container.decode(String.self, forKey: "variable_key_1_XXX")
self.variableKey2 = try container.decode(String.self, forKey: "variable_key_2_XXX")
case "YYY":
self.variableKey1 = try container.decode(String.self, forKey: "variable_key_1_YYY")
self.variableKey2 = try container.decode(String.self, forKey: "variable_key_2_YYY")
default:
throw DecodingError.unrecognizedParamValue(paramValue)
}
}
}
最后以下是您的用法:
let jsonData = """
[
{
"fixed_key1": "value1",
"fixed_key2": "value2",
"variable_key_1_XXX": "some value",
"variable_key_2_XXX": "some other value"
}
]
""".data(using: .utf8)!
// Supplying the `userInfo` dictionary is how you "configure" the JSON-decoding
let decoder = JSONDecoder()
decoder.userInfo = ["param": "XXX"]
let model = try decoder.decode([MyDataModel].self, from: jsonData)
print(model)
这篇关于使用可配置密钥的Swift 4 JSON解码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!