知用网
霓虹主题四 · 更硬核的阅读氛围

Swift解析JSON数据:从网络请求到模型映射的实用写法

发布时间:2026-02-11 03:31:57 阅读:7 次

做 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)")
}