Serde_json deserialize problem: enum icw Config

Using the config crate, when freezing the configuration, viz

let cfg = config.try_into::<MyConfig>();

where MyConfig is nested structure that includes Process,

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Process {
    Server { name: String },
    Coord  { name: String, parent: String, filename: String },
    Model  { name: String, parent: String, filename: String },
    None,
}

However, when I pass

config.set("command.process", serde_json::to_string(&p).unwrap())?;

and start to freeze config into MyConfig, I obtain the following error:

enum Process does not have variant constructor {"Coord":{"name":"MyCoord1","parent":"ParentCoord","filename":"/tmp/coord.py"}}

Yet I can do the following:

let p = Process::Coord {
            name: "MyCoord1".to_string(),
            parent: "ParentCoord".to_string(),
            filename: "/tmp/coord.py".to_string()
        };

let ps = serde_json::to_string(&p).unwrap();
let pd: Process = serde_json::from_str(&ps).unwrap();
println!("{:#?}\n{:#?}", ps, pd);

I've tried various potential 'solutions' (implementing Deserialize, Visitor, FromStr) without recourse... Help much appreciated!

BTW:

Even when taking the variant constructor out of the enum, i.e.

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Coord {
    name: String,
    parent: String,
    filename: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Process {
    Server { name: String },
    // Coord  { name: String, parent: String, filename: String },
    Coord ( Coord ),
    Model  { name: String, parent: String, filename: String },
    None,

I obtain the same error:

Error: enum Process does not have variant constructor {"Coord":{"name":"MyCoord1","parent":"ParentCoord","filename":"/tmp/coord.py"}}

I have found a rather contrived 'solution'; non-idiomatic but it works for now:

type StdResult<T, E> = std::result::Result<T, E>;
struct JSONString(String);

#[derive(Debug, Clone, Serialize, /* Deserialize*/)]
#[serde(tag = "type")]
pub enum Process {
    Server { name: String },
    Coord  { name: String, parent: String, filename: String },
    Model  { name: String, parent: String, filename: String },
    None,
}
impl<'de> de::Deserialize<'de> for Process {

    fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
        struct JSONString;

        impl<'de> de::Visitor<'de> for JSONString {
            type Value = Process;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(formatter, "a Process json string")
            }

            fn visit_str<E: de::Error>(self, s: &str) -> StdResult<Self::Value, E> {
                use serde::de::Error;

                let v: serde_json::Value = serde_json::from_str(s).unwrap();

                let p = match v["type"].as_str().unwrap() {
                    "Server" => Process::Server { name: v["name"].as_str().unwrap().to_string() },
                    "Coord"  => Process::Coord  {
                        name:         v["name"].as_str().unwrap().to_string(),
                        parent:     v["parent"].as_str().unwrap().to_string(),
                        filename: v["filename"].as_str().unwrap().to_string() },
                    "Model"  => Process::Model  {
                        name:         v["name"].as_str().unwrap().to_string(),
                        parent:     v["parent"].as_str().unwrap().to_string(),
                        filename: v["filename"].as_str().unwrap().to_string() },
                    _        => Process::None
                };

                Ok(p)
            }
        }
        deserializer.deserialize_str(JSONString)
    }
}

Idiomatically, I should be able to use derived Deserialize; I haven't figured out how...?

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