Swift中获取结构体嵌套对象的路径实现咨询
实现嵌套结构体的路径获取功能
我来帮你搞定这个需求!要让嵌套结构体的每个实例都能返回自己的完整路径,我们可以通过协议统一路径逻辑+自定义初始化器的方式实现,同时支持手动创建实例和Codable解码两种场景。
步骤1:定义路径协议
首先创建一个PathProviding协议,所有需要获取路径的结构体都要遵守它;另外扩展CodingUserInfoKey,方便解码时传递父路径:
protocol PathProviding { var path: String { get } } extension CodingUserInfoKey { static let parentPath = CodingUserInfoKey(rawValue: "parentPath")! }
步骤2:修改结构体模型
接下来调整你的Model、ModelB、ModelC结构体,让它们遵守PathProviding,并添加路径相关的初始化逻辑:
struct Model: Codable, PathProviding { struct ModelB: Codable, PathProviding { let props: [ModelC] let path: String // 手动初始化:接收父路径,为数组每个元素拼接带索引的路径 init(props: [ModelC], parentPath: String) { self.props = props.enumerated().map { index, c in ModelC(prop: c.prop, parentPath: "\(parentPath).props[\(index)]") } self.path = parentPath } // Codable解码:从Decoder获取父路径,为数组元素生成路径 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let parentPath = decoder.userInfo[.parentPath] as? String ?? "" var propsContainer = try container.nestedUnkeyedContainer(forKey: .props) var props = [ModelC]() var index = 0 while !propsContainer.isAtEnd { var elementDecoder = try propsContainer.superDecoder() let elementPath = "\(parentPath).props[\(index)]" elementDecoder.userInfo[.parentPath] = elementPath props.append(try ModelC(from: elementDecoder)) index += 1 } self.props = props self.path = parentPath } enum CodingKeys: String, CodingKey { case props } } struct ModelC: Codable, PathProviding { let prop: Int let path: String // 手动初始化:直接接收父路径 init(prop: Int, parentPath: String) { self.prop = prop self.path = parentPath } // Codable解码:从Decoder获取父路径 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.prop = try container.decode(Int.self, forKey: .prop) self.path = decoder.userInfo[.parentPath] as? String ?? "" } enum CodingKeys: String, CodingKey { case prop } // 可选:如果需要直接获取到prop的完整路径,添加这个计算属性 var propFullPath: String { "\(path).prop" } } let modelB: ModelB let path: String // 手动初始化:默认根路径为空,给ModelB传递正确的父路径 init(modelB: ModelB, parentPath: String = "") { let modelBPath = parentPath.isEmpty ? "modelB" : "\(parentPath).modelB" self.modelB = ModelB(props: modelB.props, parentPath: modelBPath) self.path = parentPath } // Codable解码:传递父路径给子结构体的Decoder init(from decoder: Decoder) throws { let parentPath = decoder.userInfo[.parentPath] as? String ?? "" let modelBPath = parentPath.isEmpty ? "modelB" : "\(parentPath).modelB" let container = try decoder.container(keyedBy: CodingKeys.self) var modelBDecoder = try container.superDecoder(forKey: .modelB) modelBDecoder.userInfo[.parentPath] = modelBPath self.modelB = try ModelB(from: modelBDecoder) self.path = parentPath } enum CodingKeys: String, CodingKey { case modelB } }
步骤3:测试使用
手动创建实例的情况
let model = Model(modelB: Model.ModelB(props: [ Model.ModelC(prop: 1, parentPath: ""), Model.ModelC(prop: 2, parentPath: ""), Model.ModelC(prop: 3, parentPath: "") ])) print(model.modelB.path) // 输出: modelB print(model.modelB.props[0].path) // 输出: modelB.props[0] print(model.modelB.props[0].propFullPath) // 输出: modelB.props[0].prop
Codable解码的情况
let json = """ { "modelB": { "props": [ {"prop": 1}, {"prop": 2}, {"prop": 3} ] } } """ guard let data = json.data(using: .utf8) else { fatalError() } let decoder = JSONDecoder() decoder.userInfo[.parentPath] = "" // 设置根路径为空 do { let decodedModel = try decoder.decode(Model.self, from: data) print(decodedModel.modelB.props[1].path) // 输出: modelB.props[1] print(decodedModel.modelB.props[1].propFullPath) // 输出: modelB.props[1].prop } catch { print("解码失败: \(error)") }
关键点说明
- 协议
PathProviding统一了所有需要路径的结构体的接口,后续扩展其他结构体时只需遵守协议即可。 - 手动初始化时,通过传递父路径自动拼接子元素的完整路径,数组元素会自动加上索引标识
[index]。 - Codable解码时,利用
Decoder的userInfo传递父路径,确保解码后的实例也能正确获取路径。
内容的提问来源于stack exchange,提问作者Peter Warbo
相关产品推荐
相关产品推荐

