You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:49:12