gpt4 book ai didi

ios - Swift: Codable - 提取单个编码 key

转载 作者:搜寻专家 更新时间:2023-10-30 22:36:57 24 4
gpt4 key购买 nike

我有以下代码来提取编码 key 中包含的 JSON:

let value = try! decoder.decode([String:Applmusic].self, from: $0["applmusic"])

这成功地处理了以下 JSON:

{
"applmusic":{
"code":"AAPL",
"quality":"good",
"line":"She told me don't worry",
}

但是,无法从以下代码中提取编码 key 为 applmusic 的 JSON:

{
"applmusic":{
"code":"AAPL",
"quality":"good",
"line":"She told me don't worry",
},
"spotify":{
"differentcode":"SPOT",
"music_quality":"good",
"spotify_specific_code":"absent in apple"
},
"amazon":{
"amzncode":"SPOT",
"music_quality":"good",
"stanley":"absent in apple"
}
}

applmusicspotifyamazon 的数据模型不同。但是,我只需要提取 applmusic 并省略其他编码键。

我的 Swift 数据模型如下:

public struct Applmusic: Codable {
public let code: String
public let quality: String
public let line: String
}

API 以完整的 JSON 响应,我不能要求它只给我所需的字段。

如何只解码json的特定部分?看来,Decodable 需要我先反序列化整个 json,所以我必须知道它的完整数据模型。

显然,其中一个解决方案是创建一个单独的 Response 模型来包含 applmusic 参数,但它看起来像一个 hack:

public struct Response: Codable {
public struct Applmusic: Codable {
public let code: String
public let quality: String
public let line: String
}
// The only parameter is `applmusic`, ignoring the other parts - works fine
public let applmusic: Applmusic
}

您能提出一种更好的方法来处理此类 JSON 结构吗?

多一点洞察力

我在通用扩展中使用了以下技术,它会自动为我解码 API 响应。因此,我更愿意概括一种处理此类情况的方法,而无需创建 Root 结构。如果我需要的键是 JSON 结构中的 3 层深度怎么办?

这是为我解码的扩展:

extension Endpoint where Response: Swift.Decodable {
convenience init(method: Method = .get,
path: Path,
codingKey: String? = nil,
parameters: Parameters? = nil) {
self.init(method: method, path: path, parameters: parameters, codingKey: codingKey) {
if let key = codingKey {
guard let value = try decoder.decode([String:Response].self, from: $0)[key] else {
throw RestClientError.valueNotFound(codingKey: key)
}
return value
}

return try decoder.decode(Response.self, from: $0)
}
}
}

API 定义如下:

extension API {
static func getMusic() -> Endpoint<[Applmusic]> {
return Endpoint(method: .get,
path: "/api/music",
codingKey: "applmusic")
}
}

最佳答案

更新:我根据这个答案对 JSONDecoder 进行了扩展,您可以在这里查看: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
}
}

您可以通过JSONDecoder ("my_model_key") 的userInfo 传递您想要的 key 。然后将其转换为 ModelResponse 中的动态 Key 以实际提取模型。

然后你可以像这样使用它:

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


奖励:深度嵌套的键

经过更多尝试后,我发现您可以使用这个修改后的 ModelResponse 轻松解码任意深度的 key :

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

关于ios - Swift: Codable - 提取单个编码 key ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50389383/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com