更新:我做了扩展JSONDecoder
从这个答案中,您可以在这里查看:https://github.com/aunnnn/NestedDecodable https://github.com/aunnnn/NestedDecodable,它允许您使用关键路径解码任意深度的嵌套模型。
你可以这样使用它:
let post = try decoder.decode(Post.self, from: data, keyPath: "nested.post")
你可以做一个Decodable
包装器(例如,ModelResponse
此处),并放置所有逻辑来提取嵌套模型,其中包含一个键:
struct DecodingHelper {
/// Dynamic key
private struct Key: CodingKey {
let stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}
let intValue: Int?
init?(intValue: Int) {
return nil
}
}
/// Dummy model that handles model extracting logic from a key
private struct ModelResponse<NestedModel: Decodable>: Decodable {
let nested: NestedModel
public init(from decoder: Decoder) throws {
let key = Key(stringValue: decoder.userInfo[CodingUserInfoKey(rawValue: "my_model_key")!]! as! String)!
let values = try decoder.container(keyedBy: Key.self)
nested = try values.decode(NestedModel.self, forKey: key)
}
}
static func decode<T: Decodable>(modelType: T.Type, fromKey key: String) throws -> T {
// mock data, replace with network response
let path = Bundle.main.path(forResource: "test", ofType: "json")!
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let decoder = JSONDecoder()
// ***Pass in our key through `userInfo`
decoder.userInfo[CodingUserInfoKey(rawValue: "my_model_key")!] = key
let model = try decoder.decode(ModelResponse<T>.self, from: data).nested
return model
}
}
您可以将您想要的密钥传递给userInfo
of JSONDecoder
("my_model_key"
)。然后将其转换为我们的动态Key
inside ModelResponse
实际提取模型。
然后你可以像这样使用它:
let appl = try DecodingHelper.decode(modelType: Applmusic.self, fromKey: "applmusic")
let amazon = try DecodingHelper.decode(modelType: Amazon.self, fromKey: "amazon")
let spotify = try DecodingHelper.decode(modelType: Spotify.self, fromKey: "spotify")
print(appl, amazon, spotify)
完整代码:https://gist.github.com/aunnnn/2d6bb20b9dfab41189a2411247d04904 https://gist.github.com/aunnnn/2d6bb20b9dfab41189a2411247d04904
奖励:深度嵌套的密钥
经过更多尝试后,我发现您可以使用此修改后轻松解码任意深度的密钥ModelResponse
:
private struct ModelResponse<NestedModel: Decodable>: Decodable {
let nested: NestedModel
public init(from decoder: Decoder) throws {
// Split nested paths with '.'
var keyPaths = (decoder.userInfo[CodingUserInfoKey(rawValue: "my_model_key")!]! as! String).split(separator: ".")
// Get last key to extract in the end
let lastKey = String(keyPaths.popLast()!)
// Loop getting container until reach final one
var targetContainer = try decoder.container(keyedBy: Key.self)
for k in keyPaths {
let key = Key(stringValue: String(k))!
targetContainer = try targetContainer.nestedContainer(keyedBy: Key.self, forKey: key)
}
nested = try targetContainer.decode(NestedModel.self, forKey: Key(stringValue: lastKey)!)
}
然后你可以像这样使用它:
let deeplyNestedModel = try DecodingHelper.decode(modelType: Amazon.self, fromKey: "nest1.nest2.nest3")
从这个json:
{
"apple": { ... },
"amazon": {
"amzncode": "SPOT",
"music_quality": "good",
"stanley": "absent in apple"
},
"nest1": {
"nest2": {
"amzncode": "Nest works",
"music_quality": "Great",
"stanley": "Oh yes",
"nest3": {
"amzncode": "Nest works, again!!!",
"music_quality": "Great",
"stanley": "Oh yes"
}
}
}
}
完整代码:https://gist.github.com/aunnnn/9a6b4608ae49fe1594dbcabd9e607834 https://gist.github.com/aunnnn/9a6b4608ae49fe1594dbcabd9e607834