Parsing là quá trình phân tích cú pháp dữ liệu có cấu trúc, trích xuất thông tin từ JSON, XML, CSV hoặc Protobuf để sử dụng trong ứng dụng. Theo Square Engineering (2025), việc lựa chọn chiến lược parsing ảnh hưởng trực tiếp đến hiệu suất của ứng dụng di động. Parsing luồng cho phép xử lý hàng megabyte dữ liệu mà không cần tải toàn bộ tài liệu vào bộ nhớ.
Những điểm chính
Parsing (phân tích cú pháp) là quá trình chuyển đổi một chuỗi ký tự thành biểu diễn có cấu trúc phù hợp cho xử lý lập trình. Trong bối cảnh phát triển di động, parsing đề cập đến việc xử lý phản hồi máy chủ, tệp cấu hình, bộ nhớ đệm cục bộ và tệp tài nguyên. Mỗi định dạng yêu cầu cách tiếp cận riêng: JSON — nhẹ và tích hợp trong SDK, XML — nghiêm ngặt với xác thực lược đồ XSD, CSV — tối giản cho dữ liệu dạng bảng, Protobuf — nhị phân với lược đồ cứng nhắc.
Bất kỳ trình phân tích nào, bất kể định dạng, đều trải qua ba giai đoạn. Phân tích từ vựng chia văn bản thô thành các token: khóa, chuỗi, số, dấu phân cách và chú thích. Phân tích cú pháp kiểm tra tính đúng đắn của ngữ pháp — cân bằng dấu ngoặc trong JSON, lồng thẻ chính xác trong XML. Xây dựng cấu trúc đầu ra tạo thành biểu diễn đối tượng: JSONObject, Map hoặc luồng token để xử lý tiếp. Theo Apple (2026), trình phân tích Foundation tích hợp trên iOS sử dụng phân tích lười biếng — chỉ xác thực phần đầu của JSON, tiết kiệm tài nguyên.
Parsing thủ công (JSONSerialization, XmlPullParser) kiểm soát hoàn toàn quy trình và hữu ích cho các định dạng không chuẩn hoặc khi cần xác thực tùy chỉnh từng trường. Thư viện tự động (JSONDecoder, Moshi, kotlinx.serialization) tăng tốc phát triển nhưng ẩn chi tiết xử lý lỗi. Sự lựa chọn phụ thuộc vào yêu cầu: nếu cấu trúc dữ liệu động hoặc không xác định trước — parsing thủ công tốt hơn. Nếu lược đồ cố định — giải tuần tự hóa tự động giảm mã 60-70%.
Parsing là một thao tác rộng hơn: có thể thực hiện theo luồng và không nhất thiết tạo đối tượng được định kiểu. Giải tuần tự hóa là một trường hợp cụ thể của parsing mà kết quả luôn là một thể hiện của lớp đã biết với các kiểu thuộc tính cụ thể. Trình phân tích JSON chia tài liệu thành Map hoặc JsonElement, trong khi bộ giải tuần tự hóa chuyển cùng JSON đó thành đối tượng User, Order hoặc Product với các trường và kiểu đã biết.
| Đặc điểm | Parsing | Giải tuần tự hóa |
|---|---|---|
| Kết quả | JsonElement, Map, luồng token | Đối tượng được định kiểu (User, Order) |
| Chế độ luồng | Có (SAX, JsonReader) | Không (toàn bộ đối tượng trong bộ nhớ) |
| Định kiểu | Tùy chọn, kiểu động | Bắt buộc, kiểu tĩnh |
| Ví dụ | XmlPullParser.next(), sự kiện SAX | json.decodeFromString<User>() |
Parsing luồng là lợi thế chính của parsing so với giải tuần tự hóa. XmlPullParser trên Android phân tích XML khi đọc, mà không tải toàn bộ tài liệu vào RAM. Điều này rất quan trọng đối với phản hồi API lớn, phân tích nguồn cấp RSS với hàng trăm mục hoặc xử lý mảng hàng nghìn đối tượng JSON từ kết xuất máy chủ. Trình phân tích luồng đọc từng token một, tiêu thụ lượng bộ nhớ không đổi bất kể kích thước tài liệu.
JSON là định dạng trao đổi dữ liệu thống trị. Trên iOS, JSONSerialization tích hợp phân tích JSON thành Any, trong khi JSONDecoder phân tích trực tiếp thành cấu trúc Codable. Trên Android, org.json tiêu chuẩn cung cấp JSONObject và JSONArray để phân tích thủ công. Gson và Moshi thêm chuyển đổi tự động. Lựa chọn giữa cách tiếp cận thủ công và tự động là quyết định kiến trúc đầu tiên của lớp mạng.
val jsonString = """{"users":[{"id":1,"name":"Alice"}]}"""
val jsonObject = JSONObject(jsonString)
val usersArray = jsonObject.getJSONArray("users")
for (i in 0 until usersArray.length()) {
val item = usersArray.getJSONObject(i)
val id = item.getInt("id")
val name = item.getString("name")
// xử lý người dùng
}
Parsing thủ công qua org.json hữu ích khi cấu trúc phản hồi không xác định trước hoặc thay đổi nhiều. Cách tiếp cận này kiểm soát hoàn toàn việc xử lý lỗi và trường nullable nhưng yêu cầu nhiều mã soạn sẵn hơn so với giải tuần tự hóa tự động. Đối với dự án nhỏ hoặc nguyên mẫu, điều này chấp nhận được, nhưng trong sản xuất, Moshi hoặc kotlinx.serialization được ưu tiên hơn.
JsonReader từ thư viện Moshi là công cụ mạnh mẽ để parsing JSON theo luồng. Nó đọc tài liệu từng token một bằng các phương thức như beginObject, nextName, nextString, nextInt và endObject. Cách tiếp cận này cho phép xử lý tài liệu ở mọi kích thước với mức tiêu thụ bộ nhớ không đổi. Theo Google (2026), parsing luồng qua JsonReader nhanh hơn 2-3 lần so với tải toàn bộ tài liệu vào JsonObject và tiêu thụ ít hơn tới 80% bộ nhớ.
val reader = JsonReader(StringReader(json))
reader.beginObject()
while (reader.hasNext()) {
val name = reader.nextName()
when (name) {
"id" -> val id = reader.nextInt()
"name" -> val name = reader.nextString()
"email" -> val email = reader.nextString()
else -> reader.skipValue()
}
}
reader.endObject()
Ví dụ cho thấy JsonReader phân tích JSON theo luồng. Mỗi lần gọi nextName() và nextInt() di chuyển con trỏ đến token tiếp theo. skipValue() bỏ qua các trường không cần thiết mà không phân tích chúng — đặc biệt hữu ích khi JSON chứa 50 trường nhưng chỉ cần 3. Kết quả là mức tiêu thụ bộ nhớ tối thiểu và tốc độ tối đa.
XML tiếp tục được sử dụng trong các dự án Android cho tài nguyên và trong các dịch vụ SOAP của ứng dụng doanh nghiệp. Trên Android, XmlPullParser tích hợp cung cấp parsing luồng với mức tiêu thụ bộ nhớ tối thiểu — nó không tải cây DOM mà tạo ra các sự kiện START_TAG, TEXT, END_TAG. iOS sử dụng XMLParser với cách tiếp cận ủy quyền: các phương thức parser(didStartElement:) và parser(foundCharacters:) xử lý các phần tử tuần tự.
CSV là định dạng đơn giản nhất để xuất và nhập dữ liệu dạng bảng. Trong ứng dụng di động, CSV được sử dụng để xuất báo cáo, tải dữ liệu tham chiếu (danh sách quốc gia, tiền tệ, mã) và đồng bộ dữ liệu ngoại tuyến. Thư viện luồng phù hợp cho parsing CSV: Apache Commons CSV và OpenCSV xử lý tệp từng dòng một, thoát dấu ngoặc kép và dấu phẩy trong giá trị. Trên iOS, parsing CSV có thể được triển khai qua phương thức phân cách NSString.components(separatedBy:) với xử lý trường hợp biên.
class CSVParser: NSObject, XMLParserDelegate {
private var currentItem: [String: String] = [:]
private var currentElement: String = ""
func parser(
_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String: String]
) {
currentElement = elementName
if elementName == "item" {
currentItem = [:]
}
}
func parser(_ parser: XMLParser,
foundCharacters string: String) {
currentItem[currentElement] = string
}
}
Ví dụ về bộ ủy quyền XMLParser trên iOS để phân tích nguồn cấp RSS. Phương thức didStartElement được gọi tại mỗi thẻ mở, foundCharacters trả về nội dung văn bản giữa các thẻ, didEndElement hoàn thành phần tử. Kiến trúc dựa trên sự kiện này cho phép xử lý XML ở mọi kích thước mà không cần tải cây DOM vào bộ nhớ — rất quan trọng đối với tài nguyên hạn chế trên thiết bị di động.
Trên Android, việc chọn trình phân tích CSV phụ thuộc vào khối lượng dữ liệu. OpenCSV hỗ trợ API linh hoạt, chú thích cho ánh xạ POJO và dấu phân cách tùy chỉnh. Apache Commons CSV ở cấp thấp hơn nhưng nhanh hơn trên tệp lớn. Đối với trường hợp đơn giản (1-2 cột), parsing thủ công qua String.split(",") với xử lý thoát là đủ. Đối với dự án sản xuất có tệp CSV lớn, OpenCSV được khuyến nghị.
Protocol Buffers của Google là định dạng tuần tự hóa nhị phân vượt trội hơn JSON về tốc độ và độ nhỏ gọn. Một thông điệp Protobuf chiếm ít không gian hơn 3-6 lần so với JSON tương đương và phân tích nhanh hơn 4-10 lần nhờ định dạng nhị phân và lược đồ cứng nhắc. Nhược điểm chính là thiếu khả năng đọc của con người và cần tạo mã từ tệp .proto qua trình biên dịch protoc.
Parsing luồng Protobuf được triển khai qua CodedInputStream — máy khách đọc các trường khi chúng đến mà không tải toàn bộ thông điệp. Mỗi trường có một thẻ (số + kiểu) và trình phân tích có thể bỏ qua các trường không xác định. Theo Google (2026), parsing luồng Protobuf tiêu thụ ít hơn tới 90% bộ nhớ khi xử lý thông điệp từ 1 MB, rất quan trọng cho thiết bị di động với RAM hạn chế. Protobuf-lite là phiên bản đặc biệt cho Android, tối ưu hóa kích thước mã được tạo.
Câu hỏi thường gặp
JSONSerialization là parsing thủ công trả về Any (Dictionary, Array) mà không định kiểu. JSONDecoder là giải tuần tự hóa tự động thành kiểu Codable. JSONSerialization cung cấp tính linh hoạt cho cấu trúc động, trong khi JSONDecoder cung cấp tốc độ và an toàn kiểu cho lược đồ đã biết. Trong sản xuất, JSONDecoder được khuyến nghị.
Parsing luồng là bắt buộc đối với phản hồi trên 500 KB, khi phân tích nguồn cấp RSS/Atom và khi xử lý tệp trên thiết bị có bộ nhớ hạn chế. Đối với phản hồi API điển hình đến 100 KB, giải tuần tự hóa tự động qua JSONDecoder hoặc Moshi tốt hơn — mã sạch hơn và phát triển nhanh hơn.
Android SDK bao gồm org.json (JSONObject, JSONArray), XmlPullParser (XML) và android.util.JsonReader. CSV, YAML và Protobuf yêu cầu thư viện bổ sung: OpenCSV cho CSV, SnakeYAML cho YAML, protobuf-javalite cho Protobuf.
Sử dụng chế độ lenient trong kotlinx.serialization hoặc JsonReader.setLenient(true) trong Moshi — nó bỏ qua dấu phẩy cuối, dấu ngoặc kép không được thoát và chú thích một dòng. Để xác thực đầy đủ, hãy triển khai kiểm thử hợp đồng qua Pact hoặc Spring Cloud Contract.
SAX (Simple API for XML) là trình phân tích XML luồng tạo ra các sự kiện startElement, characters, endElement khi duyệt tài liệu. Nó được triển khai qua XmlPullParser trên Android và XMLParser trên iOS. Nó được sử dụng trong trình đọc RSS, đồng bộ dữ liệu ngoại tuyến và xử lý tệp SVG.
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