Serde throwing deserialization errors when all fields of struct have specified default values

I am using a library along with Serde (and serde_json) to deserialize some information I get from an API; the relevant information from this library can be found here: tf2-price/src/currencies.rs at b2e8037c9963065987f886cf66e73b8c2283cf93 · juliarose/tf2-price · GitHub, but in essence it is a struct with two fields, keys and metal, that both deserialize to a struct containing an i64; metal uses a helper deserialization function which takes an input float value and multiplies it by 18 before turning it into an i64.

When attempting to deserialize information from the API where both of the fields are specified, but their values are zero, I get a parse error from serde: "Does not contain values for keys or metal" (keys and metal are the two field names). I believe some part of the code is getting confused by the specified values being the same as the defaults and assuming that the values are not specified, but I cannot figure out which part that is, or how I might override it. Does anyone have any pointers?

What's the payload that you are using? Are you using 0 or 0.0?

Could you provide us with a minimal reproducible example? Or point us to the unit test that fails? It's hard for me to figure out what's going wrong just from your description.

1 Like

Sure, here's a minimal example:

use tf2_price::Currencies;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let deserialized_currencies = serde_json::from_str::<Currencies>(r#"
        {"keys": 0, "metal": 0.11}
    "#)?;

    println!("deserialized_currencies is empty: {}", deserialized_currencies.is_empty());

    let empty_currencies = Currencies { keys: 0, metal: 0 };

    println!("empty_currencies is empty: {}", empty_currencies.is_empty());

    let empty_deserialized_currencies = serde_json::from_str::<Currencies>(r#"
        {"keys": 0, "metal": 0.0}
    "#)?;

    println!("empty_deserialized_currencies object is empty: {}", empty_deserialized_currencies.is_empty());

    Ok(())
}

Which when run gives me this output:

deserialized_currencies is empty: false
empty_currencies is empty: true
Error: Error("Does not contain values for keys or metal", line: 0, column: 0)

This is actually an explicit error in the deserialization implementation - currencies.rs - source; it's apparently logically invalid to have Currencies with only zeroes. Not sure why they made the fields public then (thus allowing to create this value manually).

4 Likes

What an odd pattern.

    #[derive(Deserialize, ...)]
    #[serde(remote = "Self")]
    pub struct Currencies {
    // ...
    }

    impl<'de> Deserialize<'de> for Currencies {
    // ...

It actually seems to be officially recommended. If I understand correctly, this means that Currencies::deserialize is the inherent function generated by derive, but <Currencies as Deserialize>::deserialize is an actual implementation with additional checks.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.