Cách sửa lỗi Rust Serde 'missing field' khi giải tuần tự hóa JSON

beginner🦀 Rust2026-06-04| Rust với các crate serde và serde_json (mọi phiên bản)

Error Message

Error("missing field `field_name`", line: 1, column: 2)
#serde#json#giải tuần tự hóa#derive#serde_json

Lỗi

Khi làm việc với crate serde của Rust để giải tuần tự hóa (deserialize) JSON, bạn có thể gặp phải lỗi panic hoặc kết quả Err trông như thế này:

Error("missing field `field_name`", line: 1, column: 2)

Điều này xảy ra khi struct Rust của bạn yêu cầu một trường cụ thể phải tồn tại trong dữ liệu đầu vào, nhưng dữ liệu JSON được cung cấp lại thiếu khóa đó. Theo mặc định, Serde rất nghiêm ngặt: nếu một trường được định nghĩa trong struct, nó phải xuất hiện trong JSON.

Nguyên nhân gốc rễ

Hãy xem xét tình huống phổ biến sau đây. Bạn có một struct đại diện cho hồ sơ người dùng:

use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct User {
    id: u32,
    username: String,
    email: String,
}

fn main() {
    let json_data = r#"{"id": 1, "username": "rustacean"}"#; // Thiếu "email"
    let user: User = serde_json::from_str(json_data).unwrap();
}

Chạy mã này sẽ kích hoạt lỗi missing field email``. Vì email được định nghĩa là một String, Serde yêu cầu nó phải có ở đó. Nếu API bạn đang sử dụng hoặc tệp cấu hình bạn đang đọc không nhất quán, ứng dụng của bạn sẽ không thể phân tích cú pháp dữ liệu.

Cách khắc phục

Có một số cách để xử lý các trường bị thiếu tùy thuộc vào cách ứng dụng của bạn nên phản ứng khi dữ liệu vắng mặt.

1. Sử dụng Option cho các trường tùy chọn

Cách chuẩn mực (idiomatic) nhất để xử lý dữ liệu có thể bị thiếu trong Rust là sử dụng kiểu Option. Khi một trường được bao bọc trong Option, Serde sẽ tự động gán cho nó giá trị None nếu khóa đó bị thiếu trong JSON.

#[derive(Deserialize, Debug)]
struct User {
    id: u32,
    username: String,
    email: Option<String>, // Bây giờ đây là trường tùy chọn
}

Với thay đổi này, ví dụ JSON trước đó sẽ được giải tuần tự hóa thành công và email sẽ đơn giản là None.

2. Sử dụng #[serde(default)] cho giá trị mặc định của kiểu dữ liệu

Đôi khi bạn không muốn dùng Option; bạn muốn một giá trị mặc định (như 0 cho số nguyên, false cho kiểu boolean, hoặc một String trống). Bạn có thể sử dụng thuộc tính #[serde(default)].

#[derive(Deserialize, Debug)]
struct Config {
    port: u16,
    #[serde(default)]
    debug_mode: bool, // Mặc định là false nếu bị thiếu
}

Nếu debug_mode bị thiếu trong JSON, Serde sẽ gọi Default::default() cho kiểu dữ liệu đó (với boolfalse).

3. Giá trị mặc định tùy chỉnh

Nếu trait Default tiêu chuẩn không cung cấp giá trị bạn cần (ví dụ: bạn muốn cổng mặc định là 8080 thay vì 0), bạn có thể chỉ định cho Serde một hàm tùy chỉnh.

#[derive(Deserialize, Debug)]
struct ServerConfig {
    #[serde(default = "default_port")]
    port: u16,
}

fn default_port() -> u16 {
    8080
}

4. Xử lý sự không nhất quán về tên gọi

Đôi khi trường đó không thực sự "thiếu" trong dữ liệu—nó chỉ được đặt tên khác với trường trong struct Rust của bạn. Hãy sử dụng #[serde(rename = "...")] để ánh xạ chúng.

#[derive(Deserialize, Debug)]
struct User {
    #[serde(rename = "user_id")]
    id: u32,
}

Nếu JSON có "user_id": 1 nhưng struct của bạn có id, bạn sẽ nhận được lỗi missing field id`` trừ khi bạn sử dụng thuộc tính rename.

Phòng ngừa và Kiểm chứng

Để kiểm chứng bản sửa lỗi, hãy luôn viết một unit test nhỏ truyền vào một chuỗi JSON thiếu trường gây lỗi. Điều này đảm bảo logic Option hoặc default của bạn hoạt động như mong đợi trước khi triển khai.

#[test]
fn test_missing_email() {
    let json = r#"{"id": 1, "username": "dev"}"#;
    let user: User = serde_json::from_str(json).expect("Nên phân tích cú pháp được mà không cần email");
    assert!(user.email.is_none());
}

Khi tôi gỡ lỗi các cấu trúc JSON lồng nhau phức tạp gây ra các lỗi này, tôi thường nhận thấy đó là lỗi cú pháp trong dữ liệu nguồn. Tôi thấy việc sử dụng JSON Formatter & Validator rất hữu ích để kiểm tra nhanh xem các khóa có thực sự khớp với những gì tôi đã viết trong mã Rust hay không. Nó nhanh hơn nhiều so với việc nhìn chằm chằm vào một khối JSON đã nén để cố gắng phát hiện một trường bị thiếu hoặc một lỗi đánh máy.

Một cái bẫy phổ biến khác là sự khác biệt giữa một trường bị thiếu và một trường mang giá trị null. Nếu JSON là {"email": null}, #[serde(default)] sẽ không hoạt động vì khóa đó có tồn tại nhưng là null. Trong trường hợp đó, bạn bắt buộc phải sử dụng Option<T>.

Related Error Notes