Adding on to oli_obk's answer, you can use Serde's enum representation to distinguish between the types.
Here, I use the internally-tagged representation to deserialize these two similar objects into the appropriate variant:
{ "driver": "file", "path": "/var/log/foo" } { "driver": "http", "port": 8080, "endpoint": "/api/bar" } use serde; // 1.0.82 use serde_derive::*; // 1.0.82 use serde_json; // 1.0.33 #[derive(Debug, Deserialize, PartialEq)] #[serde(tag = "driver")] enum Driver { #[serde(rename = "file")] File { path: String }, #[serde(rename = "http")] Http { port: u16, endpoint: String } } fn main() { let f = r#" { "driver": "file", "path": "/var/log/foo" } "#; let h = r#" { "driver": "http", "port": 8080, "endpoint": "/api/bar" } "#; let f: Driver = serde_json::from_str(f).unwrap(); assert_eq!(f, Driver::File { path: "/var/log/foo".into() }); let h: Driver = serde_json::from_str(h).unwrap(); assert_eq!(h, Driver::Http { port: 8080, endpoint: "/api/bar".into() }); } You don't have to squash it all into one enum, you can create separate types as well:
#[derive(Debug, Deserialize, PartialEq)] #[serde(tag = "driver")] enum Driver { #[serde(rename = "file")] File(File), #[serde(rename = "http")] Http(Http), } #[derive(Debug, Deserialize, PartialEq)] struct File { path: String, } #[derive(Debug, Deserialize, PartialEq)] struct Http { port: u16, endpoint: String, }