1

I am using this solution:

Is there a way to omit wrapper/root objects when deserializing objects with Serde?

to raise float values out of nested json structures:

Here is the JSON

"activitiesWon": {
    "statId": "activitiesWon",
    "basic": {
        "value": 3.0,
        "displayValue": "3"
    }
},

And here is the data structure and customer deserializer:

#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy)]
pub struct PvpStatsData {
    #[serde(rename = "activitiesWon", deserialize_with="property_to_float")]
    pub activities_won:f32,
}

pub fn property_to_float<'de, D>(deserializer: D) -> Result<f32, D::Error>
    where D: serde::de::Deserializer<'de>,
{
    #[derive(Deserialize)]
    struct Outer {
        pub basic: Inner,
    }
    
    #[derive(Deserialize)]
    struct Inner {
        pub value: f32,
    }

    let helper = Outer::deserialize(deserializer)?;
    Ok(helper.basic.value)
}

This works great. However, some of the structures / properties might not exist, and thus their field signature looks like this, with the custom deserializer which either returns a Option, or None:

#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy)]
pub struct PvpStatsData {
    #[serde(rename = "bestSingleGameKills", deserialize_with="property_to_option_float")]
    pub best_single_game_kills:Option<f32>,
}

pub fn property_to_option_float<'de, D>(deserializer: D) -> Result<Option<f32>, D::Error>
    where D: serde::de::Deserializer<'de>,
{
    println!("PARSER");
    #[derive(Deserialize, Debug, )]
    struct Outer {
        pub basic: Inner,
    }
    
    #[derive(Deserialize, Debug, )]
    struct Inner {
        pub value: f32,
    }

    Option::<Outer>::deserialize(deserializer).map(|o:Option<Outer>| match o {
        Some(e) => {
            println!("{:?}", e);
            Some(e.basic.value)
        },
        None => None,
    })
}

However, when parsing, if the property doesn't exist in json, then I get a parse error:

serde_json::Error : Error("missing field `bestSingleGameKills`", line: 1, column: 4358)

and my custom de-serialization method is never called.

Anyone know why the de-serialization method is not called? and / or how I can get it to be called when the property doesn't exist? I have other deserializers that can deal with Optional results, but I suspect this has something to do with the combination of the nested json and option.

Ive posted a playground of the example here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=022d7ca3513e411644d186518d177645

(just uncomment the second json block to see the issue)

mikechambers
  • 3,047
  • 3
  • 24
  • 31

1 Answers1

2

The default field attribute should work (I'm assuming you are ok with best_single_game_kills defaulting to None when it is absent in the json).

#[derive(Deserialize, Debug, Default, Clone, Copy)]
pub struct PvpStatsData {
    #[serde(default, rename = "activitiesWon", deserialize_with="property_to_float")]
    pub activities_won:f32,
    
    #[serde(default, rename = "bestSingleGameKills", deserialize_with="property_to_option_float")]
    pub best_single_game_kills:Option<f32>,
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a50748db2fb585a9430371a6ecd10f82

georgemp
  • 716
  • 10
  • 21
  • Yes, i ended up using default, although it defaults to 0.0 and not None. – mikechambers Jan 09 '21 at 20:00
  • hmm..in the playground linked above, it does default to None. Perhaps, you have your field setup as an f32, instead of Option (or, there is something else in the code causing it to default to 0.0)? – georgemp Jan 10 '21 at 19:43