Rust Serde deserialization: how can I make sure a field does NOT exist and throw an error if it does exist?

sudoExclaimationExclaimation

I want to make sure certain fields are NOT specified when deserializing. I want to throw an error during deserialization if the field is present. Note that I am not asking about the field being None. I am asking about making sure the field itself isn't specified.

I asked ChatGPT and it gave me the following:

#[derive(Deserialize)]
struct MyStruct {
    #[serde(default, deserialize_with = "check_field")]
    required_field: Option<String>,
    other_field: String,
}

fn check_field<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
    D: Deserializer<'de>,
{
    if deserializer.is_human_readable() {
        // If deserialization is happening from a human-readable format, throw an error
        Err(serde::de::Error::custom("Required field should not be present"))
    } else {
        // If deserialization is happening from a non-human-readable format, proceed normally
        Ok(None)
    }
}

While this does seem to work, I am not sure if deserializer.is_human_readable() is the right way? I read the documents and it says the following:

"Some types have a human-readable form that may be somewhat expensive to construct, as well as a binary form that is compact and efficient. Generally text-based formats like JSON and YAML will prefer to use the human-readable one and binary formats like Postcard will prefer the compact one."

So this doesn't seem right as if the field is in binary form, then it would allow it to exist.

Is there a better/correct way?

drewtato

I believe you can just unconditionally error. The function will only run if the field name has been found, at which point the data is definitely invalid.

#[derive(Deserialize, Debug)]
pub struct MyStruct {
    #[serde(default, deserialize_with = "deny_field", rename = "required_field")]
    _required_field: (),
    pub other_field: String,
}

fn deny_field<'de, D>(_deserializer: D) -> Result<(), D::Error>
where
    D: Deserializer<'de>,
{
    Err(serde::de::Error::custom(
        "Required field should not be present",
    ))
}

I've also cleaned up some dead code warnings. You can either start the field name with an underscore and rename it, or annotate it with #[allow(dead_code)]. The underscore method has the advantage of being visibly unused if encountered elsewhere, so I've done that.

It seems to work with all the relevant cases.

use serde_json::{from_value, json};

fn main() {
    let required_int = json! ({
        "required_field": 30,
        "other_field": "other data",
    });
    let err = from_value::<MyStruct>(required_int).unwrap_err();
    dbg!(err);

    let required_null = json! ({
        "other_field": "other data",
        "required_field": null,
    });
    let err = from_value::<MyStruct>(required_null).unwrap_err();
    dbg!(err);

    let required_string = json! ({
        "required_field": "some_data",
        "other_field": "other data",
    });
    let err = from_value::<MyStruct>(required_string).unwrap_err();
    dbg!(err);

    let valid = json! ({
        "other_field": "other data",
    });
    let my_struct: MyStruct = from_value(valid).unwrap();
    dbg!(my_struct);

    let valid_with_extra = json! ({
        "another_field": "another data",
        "other_field": "other data",
    });
    let my_struct: MyStruct = from_value(valid_with_extra).unwrap();
    dbg!(my_struct);
}

This will only work for self-describing formats like JSON, but other formats usually have no concept of "field names", so this is probably fine.

Also remember to add #[serde(skip_serializing)] to the field if you decide to derive Serialize.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

TeamViewer does not start automatically, how do I make sure it does?

How can i make sure that my Container does not go outside my screen using Transform.transalte

How can I make sure Pandas does not interpret a numeric string as a number in Pandas?

Swift: How can I make sure the seque does not run before the http task is finished?

How can I make sure my access token to Instagram API does not expire?

How can I filter an array to make sure it does contain positions of another array

How can I make sure Excel does not change number format of a data label in other language versions?

How can I serialise Rust object to Rust literals using serde?

How to make a hashtable throw an error if key does not exists?

How to make sure selenium does not close the browser?

Rust & Serde JSON deserialization examples?

why does it throw an error when i compile it?

Why does R's predict function throw an error for glmerMod models using bs splines, and how can I obtain model predictions in this case?

How can I deserialize an optional field with custom functions using Serde?

How can I make sure the return is an array?

Why does julia throw an LoadError in addition to the error I want to throw

How can I make to throw this catch with JUnit

How does toArray() function make sure that a list of long type can't be converted to array of Integer

if an option "ALL" is selected, how do I make sure other options does not run?

How do I make sure flex box content does not extend further than the end of the pages original height?

Mysql select for update - it is not locking the target rows. How do I make sure it does?

How do I make sure GridSearchCV first does the cross split and then the imputing?

How do I make sure erroneous code does not build in a CI process?

How would I make sure that a string doesn't contain letters, and keep the program running if it does?

Can expect make sure a certain log does not appear?

How to make Mongoose throw an error when pulling an entry via findByIdAndUpdate() does not find anything in the db?

how do i make sure the field is not filled in with empty spaces only?

How to find out what error I'm dealing with when Kohana site does nothing but throw error 500?

Why does "throw Error" make a function run as synchronous?

How to make an `Option` to be None by inner field in serde?

TOP Ranking

HotTag

Archive