ebarnard / rust-plist

A rusty plist parser.
MIT License
71 stars 42 forks source link

Deserialize from `Dictionary` #79

Closed probablykasper closed 1 year ago

probablykasper commented 2 years ago

Would be great to be able to deserialize a Dictionary into a struct. For example, in serde_json there's serde_json::from_value().

My use case

I have a plist with a bunch of dictionaries that I'd like to parse into enums of structs.

#[derive(Deserialize, Debug)]
#[serde(untagged)]
struct PlistFile {
  changes: Vec<Change>,
}

#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum Change {
  Addition(Addition),
  // + other types I don't know about
}

#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
struct Addition {
  example_field: String,
}

The problem is I don't know about all the possible variants of Change, so I need to figure that out. If I deserialize to the structs above, I get an unhelpful error message:

Unable to parse response: Serde("data did not match any variant of untagged enum Change")

So my plan was to instead deserialize to Vec<Dictionary>, then loop through the dicts and deserialize them individually - Then, if it fails, I can log the dict's fields to find out what I need to add.

probablykasper commented 2 years ago

Did manage to do it in a roundabout way by serializing then deserializing:

fn deserialize_value<T: DeserializeOwned>(value: &Value) -> Result<T, plist::Error> {
  let mut buf_writer = BufWriter::new(Vec::new());
  value.to_writer_binary(&mut buf_writer).unwrap();
  let bytes = buf_writer.into_inner().unwrap();
  let cursor = Cursor::new(bytes);
  let change: T = plist::from_reader(cursor)?;
  Ok(change)
}
ebarnard commented 2 years ago

Have a look at serde-transcode it seems to be designed for this use case.

probablykasper commented 2 years ago

Thanks for the recommendation, but I'm not too sure if that's possible to use? It seems like serde-transcore is for deserializing then serializing, but the issue is I can't seem to deserialize Value or Dictionary.

I see that Value does actually implement serde::Deserialize, so maybe there's some way to deserialize it?

ebarnard commented 2 years ago

Looking at serde-json it seems as though Value would need to implement Deserializer. There might be a quick and dirty way to do this using something like: https://github.com/ebarnard/rust-plist/blob/4643d95b2ec21257214976edd7655edda19acd55/src/serde_tests.rs#L12