做 iOS 开发时,几乎每天都要跟 JSON 打交道——服务器返回的用户信息、商品列表、订单状态,基本都是 JSON 格式。Swift 本身不带“一键解析”功能,但用好 JSONDecoder 和合适的模型结构,几行代码就能把一串字符串变成可操作的对象。
最常见场景:从 API 拿回一段 JSON 字符串
比如调用天气接口,返回:
{"city":"北京","temperature":23.5,"condition":"晴"}
先定义一个匹配的 Swift 结构体:
struct Weather: Codable {
let city: String
let temperature: Double
let condition: String
}
注意:只要属性名和 JSON key 完全一致,且都遵循 Codable,不用额外写 init(from:) 或 keyEncodingStrategy。
解析三步走:data → decoder → model
假设你已经拿到 Data(比如 URLSession 回调里的 data):
guard let data = data else { return }
let decoder = JSONDecoder()
do {
let weather = try decoder.decode(Weather.self, from: data)
print(weather.city, weather.temperature) // 北京 23.5
} catch {
print("解析失败:\(error)")
}
字段名不一致?用 CodingKeys
如果后端返回的是 temp_c 而不是 temperature,加个 CodingKeys 映射就行:
struct Weather: Codable {
let city: String
let temperature: Double
let condition: String
enum CodingKeys: String, CodingKey {
case city, condition
case temperature = "temp_c"
}
}
嵌套 JSON 怎么办?继续拆成结构体
比如返回:
{"user":{"name":"张三","profile":{"age":28,"avatar_url":"https://..."}}}
就对应两层结构:
struct UserResponse: Codable {
let user: UserInfo
}
struct UserInfo: Codable {
let name: String
let profile: Profile
}
struct Profile: Codable {
let age: Int
let avatarURL: String
enum CodingKeys: String, CodingKey {
case age
case avatarURL = "avatar_url"
}
}
数组也一样处理
返回商品列表?直接 decode 成 [Product].self:
struct Product: Codable {
let id: Int
let title: String
let price: Double
}
// 解析数组
let products = try decoder.decode([Product].self, from: data)
遇到 null 或缺失字段?用可选类型
如果某个字段可能为 null 或压根没传,声明成 String? 或 Int? 即可,JSONDecoder 会自动处理:
struct Product: Codable {
let id: Int
let title: String
let discountPrice: Double?
}
哪怕 JSON 里是 "discount_price": null 或干脆没这字段,也不会崩溃。
小技巧:快速生成 Codable 模型
复制一段 JSON,粘贴到 quicktype.io,选 Swift,它能自动生成带 CodingKeys 的完整结构体——尤其适合字段多、嵌套深的接口,省得手敲还拼错。
别忘了错误处理不是摆设
真实项目中,后端字段偶尔改名、类型变 int 成 string、甚至整个结构乱掉。建议在关键解析处加日志:
catch DecodingError.keyNotFound(let key, _) {
print("缺少字段:\(key)")
} catch DecodingError.typeMismatch(let type, _) {
print("类型不匹配:期望 \(type), 但收到其他值")
} catch {
print("未知解析错误:\(error)")
}