Require a field to be an integer or null, but not missing in JSON
Asked Answered
S

1

5

I want to deserialize the following struct using serde_json. The parent_id field should accept an integer or a null, but I want it to return an error if the field is missing.

#[derive(Debug, Serialize, Deserialize)]
pub struct Group {
   pub name: String,
   pub parent_id: Option<i32>,
}

// this is accepted
serde_json::from_value(json!({
   "name": "Group 1",
   "parent_id": null,
});

// this should return an error
serde_json::from_value(json!({
   "name": "Group 1",
});

I tried using the code above, but parent_id would be deserialized into a None even though it doesn't exist.

Submultiple answered 9/7, 2024 at 9:51 Comment(0)
T
7

You can deserialize_with with custom function to get the expected behaviour.

use serde::{Deserialize, Deserializer, Serialize};
use serde_json::json;


#[derive(Debug, Serialize, Deserialize)]
pub struct Group {
   pub name: String,
   #[serde(deserialize_with = "Option::deserialize")]
   pub parent_id: Option<i32>,
}

fn main() {
    // thread 'main' panicked at src/main.rs:25:10:
    // called `Result::unwrap()` on an `Err` value: Error("missing field 
    // `parent_id`", line: 0, column: 0)
    let r: Group = serde_json::from_value(json!({
        "name": "Group 1",
     })).unwrap();
    println!("{:?}", r);

}
Truett answered 9/7, 2024 at 9:56 Comment(2)
This is kind of funny since it seems tautological: deserializing any field will use its Type::deserialize implementation to do it. But this works because Option<_> is special-cased by serde with #[serde(default)] because its often intended to handle missing values. However, adding the deserialize_with skips that special-casing so that it no longer defaults to None (and therefore complain when missing).Diandre
Incorporating the explanation by @Diandre in the answer would greatly improve it!Unsustainable

© 2022 - 2025 — McMap. All rights reserved.