1. 使用 Codable 协议(推荐)
基本使用
swift
// 定义模型
struct User: Codable {
let id: Int
let name: String
let email: String?
let age: Int?
// 如果键名不同,使用 CodingKeys
private enum CodingKeys: String, CodingKey {
case id, name, email, age
}
}
// 解析 JSON
let jsonString = """
{
"id": 1,
"name": "John",
"email": "john@example.com",
"age": 30
}
"""
do {
let jsonData = Data(jsonString.utf8)
let user = try JSONDecoder().decode(User.self, from: jsonData)
print(user.name) // 输出: John
} catch {
print("解码错误: \(error)")
}
// 编码为 JSON
let user = User(id: 1, name: "Jane", email: nil, age: 25)
do {
let jsonData = try JSONEncoder().encode(user)
let jsonString = String(data: jsonData, encoding: .utf8)
} catch {
print("编码错误: \(error)")
}
处理嵌套 JSON
swift
struct Post: Codable {
let title: String
let author: User
let tags: [String]
}
// 自定义日期格式
struct Event: Codable {
let name: String
let date: Date
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
// 或自定义格式
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
decoder.dateDecodingStrategy = .formatted(formatter)
2. 使用 JSONSerialization
swift
// 解析 JSON
let jsonString = """
{
"name": "Swift",
"version": 5.0
}
"""
if let data = jsonString.data(using: .utf8) {
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
let name = json["name"] as? String
let version = json["version"] as? Double
print("Name: \(name ?? ""), Version: \(version ?? 0)")
}
} catch {
print("JSON 解析错误: \(error)")
}
}
// 创建 JSON
let dict: [String: Any] = [
"name": "iOS",
"version": 15.0,
"features": ["SwiftUI", "ARKit"]
]
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
} catch {
print("JSON 创建错误: \(error)")
}
3. 处理复杂场景
自定义解码
swift
struct Product: Codable {
let id: Int
let name: String
let price: Double
let discountPrice: Double?
// 自定义解码逻辑
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
price = try container.decode(Double.self, forKey: .price)
// 处理可选值
discountPrice = try container.decodeIfPresent(Double.self, forKey: .discountPrice)
// 数据验证
if price < 0 {
throw DecodingError.dataCorruptedError(
forKey: .price,
in: container,
debugDescription: "价格不能为负数"
)
}
}
}
使用 Property Wrapper
swift
@propertyWrapper
struct DefaultEmptyString: Codable {
var wrappedValue: String
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
wrappedValue = (try? container.decode(String.self)) ?? ""
}
}
struct Profile: Codable {
@DefaultEmptyString var nickname: String
@DefaultEmptyString var bio: String
}
4. 网络请求中的 JSON 处理
swift
import Foundation
struct NetworkService {
func fetchData<T: Decodable>(from url: URL) async throws -> T {
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw URLError(.badServerResponse)
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase // 处理蛇形命名
return try decoder.decode(T.self, from: data)
}
}
// 使用示例
struct APIResponse<T: Codable>: Codable {
let status: Int
let message: String
let data: T
}
// 调用
Task {
do {
let url = URL(string: "https://api.example.com/users")!
let response: APIResponse<[User]> = try await NetworkService().fetchData(from: url)
print(response.data)
} catch {
print("错误: \(error)")
}
}
5. 使用第三方库
Swift Package Manager 添加依赖
swift
// 在 Package.swift 中添加
dependencies: [
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "5.0.0")
]
SwiftyJSON 示例
swift
import SwiftyJSON
let json = JSON(parseJSON: jsonString)
let name = json["user"]["name"].stringValue
let age = json["user"]["age"].intValue
// 安全访问
if let email = json["user"]["email"].string {
print(email)
}
6. 最佳实践
-
始终使用 do-catch 处理错误
-
使用可选类型处理可能缺失的数据
-
为 API 响应创建专门的模型
-
使用 keyDecodingStrategy 处理命名约定
-
考虑使用 Result 类型
swift
enum NetworkError: Error {
case invalidURL
case noData
case decodingError
}
func parseJSON<T: Decodable>(data: Data) -> Result<T, NetworkError> {
do {
let decodedData = try JSONDecoder().decode(T.self, from: data)
return .success(decodedData)
} catch {
return .failure(.decodingError)
}
}
7. 性能优化技巧
swift
// 重用 JSONDecoder/JSONEncoder
class JSONManager {
static let shared: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}()
}
// 使用 lazy 延迟解码
struct LazyDecoded<T: Decodable>: Decodable {
let value: T
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.value = try container.decode(T.self)
}
}
这些方法涵盖了 Swift 中处理 JSON 的主要场景。Codable 是现代 Swift 开发中最推荐的方式,因为它类型安全、代码简洁。