JSONSerialization — một lớp iOS tích hợp sẵn từ framework Foundation được thiết kế để chuyển đổi JSON thành các đối tượng Foundation và ngược lại. API này là cơ chế cơ bản để làm việc với JSON trên các nền tảng Apple mà không cần thư viện bên thứ ba, hỗ trợ phân tích từ điển, mảng và các kiểu nguyên thủy. Theo Apple Developer, 2024, JSONSerialization hỗ trợ làm việc với Data, luồng và các tùy chọn đọc để xử lý dữ liệu JSON linh hoạt.
Điểm chính
JSONSerialization là một lớp của framework Foundation có sẵn trên iOS, macOS, tvOS và watchOS. Nó cung cấp các phương thức để chuyển đổi JSON Data thành các đối tượng Foundation (NSDictionary, NSArray, NSString, NSNumber) và ngược lại. Lớp này xuất hiện trong iOS 5 và cho đến khi Codable (Swift 4) ra đời, nó vẫn là cách chính để làm việc với JSON trên các nền tảng Apple. Mặc dù đã cũ, JSONSerialization vẫn phù hợp trong các dự án Objective-C kế thừa và trong các tình huống cần xử lý JSON động mà không có lược đồ mô hình cố định.
Mặc dù Codable đã ra đời, JSONSerialization vẫn phù hợp trong nhiều tình huống. Cấu trúc JSON động — khi định dạng phản hồi thay đổi hoặc không xác định trước — yêu cầu truy cập từ điển qua khóa, điều này dễ thực hiện hơn qua JSONSerialization. Lớp này cũng được sử dụng trong các dự án Objective-C nơi Codable không khả dụng và khi làm việc với luồng để phân tích tăng dần các tệp JSON lớn. Trong các bài kiểm tra và mô phỏng, isValidJSONObject và data(withJSONObject:options:) cho phép tạo nhanh các fixture JSON mà không cần thư viện bên thứ ba, tăng tốc phát triển và tạo mẫu thử.
import Foundation
// Cấu trúc cơ bản sử dụng JSONSerialization
let jsonString = """
{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
"""
guard let jsonData = jsonString.data(using: .utf8) else {
return
}
do {
let json = try JSONSerialization
.jsonObject(with: jsonData,
options: .mutableContainers)
print(json)
} catch {
print("Lỗi phân tích JSON: \(error)")
}
JSONSerialization cung cấp bốn phương thức chính để làm việc với JSON. Phương thức chính là jsonObject(with:options:), chuyển đổi Data thành các đối tượng Foundation. Phương thức data(withJSONObject:options:) thực hiện tuần tự hóa ngược. isValidJSONObject(_:) kiểm tra xem một đối tượng có thể được tuần tự hóa hay không. writeJSONObject(_:to:options:error:) ghi JSON trực tiếp vào luồng. Để đọc JSON từ InputStream, có phương thức jsonObject(with:options:) chấp nhận luồng thay vì Data, thuận tiện khi tích hợp với các yêu cầu mạng trả về dữ liệu luồng.
Phương thức jsonObject chấp nhận Data và trả về Any — thường là NSDictionary hoặc NSArray. Để sử dụng an toàn, kết quả được chuyển đổi sang kiểu mong đợi thông qua ép kiểu có điều kiện. Phương thức data chấp nhận một đối tượng Foundation và trả về Data với biểu diễn JSON. Tùy chọn .prettyPrinted thêm định dạng với thụt đầu dòng để dễ đọc.
let jsonString = """
{
"products": [
{"id": 1, "name": "iPhone", "price": 999},
{"id": 2, "name": "iPad", "price": 799}
]
}
"""
let data = Data(jsonString.utf8)
if let json = try? JSONSerialization
.jsonObject(with: data) as? [String: Any],
let products = json["products"] as? [[String: Any]] {
for product in products {
if let name = product["name"] as? String {
print("Sản phẩm: \(name)")
}
}
}
// Tuần tự hóa ngược: đối tượng -> JSON
let outputDict: [String: Any] = ["status": "ok", "count": 42]
if let outputData = try? JSONSerialization
.data(withJSONObject: outputDict,
options: .prettyPrinted) {
String(data: outputData, encoding: .utf8)
}
Phân tích cơ bản một từ điển với các kiểu nguyên thủy là thao tác phổ biến nhất với JSONSerialization. Sau khi nhận Data qua URLSession, nhà phát triển gọi jsonObject và chuyển đổi kết quả sang kiểu mong đợi. Đối với mảng các đối tượng, sử dụng ép kiểu sang [[String: Any]], sau đó mỗi phần tử được xử lý trong một vòng lặp. Cách tiếp cận này linh hoạt nhưng yêu cầu quản lý kiểu thủ công.
Các API thực tế trả về các đối tượng JSON lồng nhau phức tạp với mảng, ngày tháng và các trường tùy chọn. JSONSerialization xử lý chính xác mọi độ sâu lồng nhau, nhưng nhà phát triển phải tự chuyển đổi từng cấp độ sang kiểu yêu cầu. Để đơn giản hóa tác vụ này, Apple khuyên dùng Codable cho dữ liệu có kiểu và chỉ dùng JSONSerialization cho cấu trúc động.
// Phân tích phản hồi API
func parseUserResponse(data: Data) {
do {
guard let json = try JSONSerialization
.jsonObject(with: data) as? [String: Any]
else { return }
guard let userId = json["id"] as? Int,
let name = json["name"] as? String
else {
throw ParsingError.missingField
}
print("Người dùng: \(name) (ID: \(userId))")
} catch let error as ParsingError {
print("Phân tích thất bại: \(error)")
} catch {
print("Lỗi không mong đợi: \(error)")
}
}
enum ParsingError: Error {
case missingField
case invalidType
}
JSONSerialization ném lỗi khi JSON không hợp lệ, không khớp kiểu hoặc vượt quá độ sâu lồng nhau. Các lỗi thuộc kiểu CocoaError và chứa mã mô tả vấn đề. Nhà phát triển phải xử lý chúng thông qua cấu trúc do-catch, nếu không ứng dụng sẽ bị treo. Các lỗi phổ biến nhất là: NSPropertyListReadCorruptError (JSON không hợp lệ) và NSPropertyListReadUnknownError. Mỗi loại lỗi yêu cầu chiến lược xử lý riêng: đối với định dạng không hợp lệ, yêu cầu gửi lại dữ liệu và đối với không khớp cấu trúc, cập nhật mô hình phân tích.
JSON không hợp lệ — nguyên nhân phổ biến nhất gây thất bại: thiếu dấu phẩy, ký tự thừa hoặc dấu ngoặc kép không được thoát sẽ phá vỡ toàn bộ quá trình phân tích. Loại lỗi thứ hai là không khớp với cấu trúc mong đợi: ví dụ, máy chủ trả về một mảng thay vì một từ điển. JSONSerialization.fragmentsAllowed cho phép đọc JSON có gốc không phải là từ điển hoặc mảng mà là một giá trị nguyên thủy. Nhà phát triển cũng có thể gặp lỗi vượt quá độ sâu lồng nhau khi JSON chứa quá nhiều cấp phân cấp.
JSONSerialization cung cấp một số tùy chọn để cấu hình phân tích. .mutableContainers trả về NSMutableDictionary và NSMutableArray thay vì các phiên bản bất biến, hữu ích khi sửa đổi dữ liệu sau khi phân tích. .mutableLeaves làm cho các giá trị chuỗi có thể thay đổi. .fragmentsAllowed cho phép JSON có gốc không phải là đối tượng hoặc mảng mà là chuỗi hoặc số — thuận tiện cho các phản hồi API đơn giản. Các tùy chọn .withoutEscapingSlashes và .sortedKeys có sẵn cho phương thức data(withJSONObject:options:), kiểm soát định dạng của JSON đã tuần tự hóa. Các tùy chọn được truyền dưới dạng mặt nạ bit, cho phép kết hợp nhiều giá trị thông qua toán tử | để cấu hình phân tích linh hoạt.
// Xử lý các loại lỗi khác nhau
func safeParse(jsonData: Data) {
do {
let object = try JSONSerialization
.jsonObject(with: jsonData,
options: .fragmentsAllowed)
if let dictionary = object as? [String: Any] {
print("Từ điển với \(dictionary.count) khóa")
} else if let array = object as? [Any] {
print("Mảng với \(array.count) mục")
}
} catch CocoaError.propertyListReadCorrupt {
print("Dữ liệu JSON bị hỏng")
} catch let error as CocoaError {
print("Lỗi Cocoa: \(error)")
} catch {
print("Lỗi không xác định: \(error)")
}
}
// Kiểm tra tính hợp lệ của đối tượng trước khi tuần tự hóa
let testObject: [String: Any] = ["key": "value", "nested": ["a": 1]]
if JSONSerialization.isValidJSONObject(testObject) {
print("Đối tượng JSON hợp lệ")
}
Hiệu suất của JSONSerialization phụ thuộc vào kích thước dữ liệu và tần suất gọi. Đối với một lần phân tích một phản hồi máy chủ nhỏ, sự khác biệt là không đáng kể, nhưng khi xử lý hàng chục megabyte JSON hoặc các lời gọi thường xuyên trong vòng lặp, cần xem xét chi phí ép kiểu. JSONSerialization hoạt động đồng bộ trong luồng hiện tại, do đó đối với các tài liệu lớn, nên chuyển việc phân tích sang hàng đợi nền qua DispatchQueue.global(). Thay vào đó, bạn có thể sử dụng InputStream để xử lý luồng mà không cần tải toàn bộ tệp vào bộ nhớ, điều này rất quan trọng đối với các ứng dụng có tài nguyên hạn chế. Để ghi JSON vào tệp hoặc luồng mạng, phương thức writeJSONObject(_:to:options:error:) cho phép gửi trực tiếp dữ liệu đã tuần tự hóa đến OutputStream mà không tạo ra đối tượng Data trung gian, giảm mức tiêu thụ bộ nhớ khi làm việc với các tài liệu lớn.
Câu hỏi thường gặp
JSONSerialization là một lớp Foundation để chuyển đổi JSON Data thành các đối tượng Foundation (NSDictionary, NSArray) và ngược lại. Nó hoạt động trên iOS, macOS, tvOS và watchOS mà không cần thư viện bổ sung.
Codable là một giao thức Swift cho tuần tự hóa tự động có kiểu, biên dịch thành mã an toàn về kiểu. JSONSerialization làm việc với các kiểu Any động và yêu cầu ép kiểu thủ công. Codable được ưa tiên cho các dự án mới, JSONSerialization cho Objective-C và dữ liệu động.
Sử dụng cấu trúc do-catch khi gọi jsonObject. Các lỗi JSONSerialization thuộc về CocoaError. Để gỡ lỗi, hãy kiểm tra NSPropertyListReadCorruptError, cho biết định dạng dữ liệu JSON không hợp lệ.
Có, JSONSerialization hỗ trợ mọi độ sâu lồng nhau của từ điển và mảng. Tất cả các đối tượng lồng nhau được chuyển đổi thành các kiểu Foundation tương ứng (NSDictionary, NSArray, NSString, NSNumber), giữ nguyên cấu trúc JSON ban đầu.
JSONSerialization phù hợp cho cấu trúc JSON động, trong các dự án Objective-C, khi làm việc với luồng và để xác thực JSON qua isValidJSONObject. Đối với cấu trúc có kiểu với lược đồ đã biết, Codable được ưa tiên hơn.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm