Updating object fields given dynamic JSON

I have a use case where I would like to update an objects specific fields given dynamic JSON.

For example:

struct Transmitter {
enabled: bool, // user modifiable (setpoint)
deadband: f64, // user modifiable (setpoint)
alarm: bool, // user modifiable (setpoint)
accum: i32, // non-modifiable by user

}

From a GUI, the user would send a "setpoint" or modification containing only what was changed to the instance. For example, if the user wanted to enable the alarm field, the GUI would send the following as JSON: "{'alarm': true}". Alternatively, if the user enabled the alarm and change the deadband, the following would be sent: "{'alarm': true, 'deadband': 500}".

Is it possible or is there a way to modifiable fields of a struct given only JSON. Possibly a use case in serde?

Thanks!

You can make a struct like this:

#[derive(Deserialize)]
struct TransmitterUpdate {
    #[serde(default)]
    enabled: Option<bool>,

    #[serde(default)]
    deadband: Option<f64>,

    #[serde(default)]
    alarm: Option<bool>,
}

Then you can update each field that isn't None.

Notice that #[serde(default)] will insert the value of Default::default() when the field is missing, and for options, that happens to be None.

1 Like

I've used a following pattern once:

  • deserialize current state of struct to json
  • deserialize "patch" to json
  • combine two jsons
  • deserialize from json
#[derive(Serialize, Deserialize)]
struct Config { /* ... */ }; // The struct to update – in your case, a Transmitter

use serde_json::Value;

fn amend(config: Config, new_rules: &Value) -> crate::Result<Config> {
    let config: Value = serde_json::to_value(&config).unwrap();

    let mut config: BTreeMap<String, Value> = serde_json::from_value(config).unwrap();
    let new_rules: BTreeMap<String, Value> = serde_json::from_value(new_rules.clone())?;
    for (k, v) in new_rules {
        config.insert(k, v);
    }

    let config: Value = serde_json::to_value(&config).unwrap();
    Ok(serde_json::from_value(config)?)
}

It's not the fastest way, and I certainly consider it more of a hack than a solution, but hey, it works!

Also, there was a recent discussion on reddit on similar topic https://www.reddit.com/r/rust/comments/fbe0h3/serde_deserializing_into_an_existing_object/

1 Like

That doesn't look too bad actually...It's not something that will be happening all that often anyways so I'm not too concerned with speed. I'll definitely play around with that method!

I ended up using the pointer_mut method of serde_json. Worked as needed. Seemed much cleaner!

Thanks

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.