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

Swift 4中如何用Codable替代ObjectMapper解析嵌套JSON

用Codable实现无嵌套结构体的点语法访问

嘿,我懂你想要的——不用为JSON里的每一层嵌套都定义专属结构体,像用ObjectMapper那样灵活地通过点语法访问属性对吧?刚好Swift的**动态成员查找(Dynamic Member Lookup)**特性可以帮我们实现这个需求,配合Codable就能摆脱繁琐的结构体定义!

方案:通用JSON容器 + 动态成员查找

我们可以定义一个带@dynamicMemberLookup修饰的通用Codable结构体,它能自动处理任意嵌套的JSON结构,让你直接用点语法访问属性:

@dynamicMemberLookup
struct JSONContainer: Codable {
    private let container: KeyedDecodingContainer<CodingKeys>
    
    // 自定义CodingKey,支持任意字符串键
    private struct CodingKeys: CodingKey {
        var stringValue: String
        init?(stringValue: String) { self.stringValue = stringValue }
        
        var intValue: Int?
        init?(intValue: Int) { return nil }
    }
    
    // 解码初始化方法
    init(from decoder: Decoder) throws {
        self.container = try decoder.container(keyedBy: CodingKeys.self)
    }
    
    // 编码方法(如果不需要编码可以省略)
    func encode(to encoder: Encoder) throws {
        var targetContainer = encoder.container(keyedBy: CodingKeys.self)
        for key in container.allKeys {
            try targetContainer.encode(container.decode(JSONContainer.self, forKey: key), forKey: key)
        }
    }
    
    // 动态成员查找的核心:通过属性名动态解码
    subscript<T: Codable>(dynamicMember member: String) -> T? {
        get {
            guard let key = CodingKeys(stringValue: member) else { return nil }
            return try? container.decode(T.self, forKey: key)
        }
    }
}

如何使用这个容器

用它解析你提供的JSON字符串,直接通过点语法访问任意嵌套属性,完全不用定义Health、Size这类结构体:

let jsonString = """
{
    "health": {
        "size":{
            "width":150,
            "height":150
        },
        "name":"Apple"
    }
}
"""

do {
    let rootContainer = try JSONDecoder().decode(JSONContainer.self, from: jsonString.data(using: .utf8)!)
    
    // 直接用点语法访问嵌套属性,指定类型即可
    let healthName: String? = rootContainer.health.name
    let sizeWidth: Double? = rootContainer.health.size.width
    let sizeHeight: Double? = rootContainer.health.size.height
    
    print("Name: \(healthName ?? "Unknown")") // Name: Apple
    print("Size: \(sizeWidth ?? 0)x\(sizeHeight ?? 0)") // Size: 150.0x150.0
} catch {
    print("解码失败:\(error.localizedDescription)")
}

注意事项

  • 访问时需要明确指定类型(比如String?、Double?),因为Swift需要知道你要解码成什么类型;
  • 如果JSON里的类型和你指定的不匹配(比如你原来ObjectMapper里把name定义成Double?但JSON里是字符串),解码会返回nil,所以要确保类型对应;
  • 这个容器支持无限嵌套,不管JSON有多深,都能通过点语法一层层访问。

这样就完美替代了ObjectMapper的写法,又不用写一堆重复的结构体,是不是很方便?

内容的提问来源于stack exchange,提问作者salman siddiqui

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:58:55