I am trying to use the try_from feature of serde but it is not behaving as I expected.
As you can see in my example, I want to convert JSON message containing a result field, indicating success or not, into a Result<T> without this result field.
use serde::Deserialize;
use serde_json as json;
use std::convert::TryFrom;
#[derive(Deserialize, Debug)]
#[serde(try_from = "Response<Device>")]
struct Device {
model: String,
version: String,
}
#[derive(Deserialize)]
struct Response<T> {
result: String,
#[serde(flatten)]
inner: T,
}
impl TryFrom<Response<Device>> for Device {
type Error = String;
fn try_from(response: Response<Device>) -> Result<Device, String> {
if response.result == "success" {
Ok(response.inner)
} else {
Err(response.result)
}
}
}
fn main() {
let msg = r#"{
"result": "success",
"model": "foobar",
"version": "v1.0.0"
}"#;
let _: Device = json::from_str(msg).unwrap();
let msg = r#"{
"result": "error"
}"#;
let _: Device = json::from_str(msg).unwrap();
}
I guess you are talking about the second example with "result": "error". If you run the example you'll see that the issue arise at the first example. It seems to complains about a missing result field in the Device struct. My understanding of how it works is that it deserialize the message into Response<Device> where all necessary fields are present, then do try_from to convert into a Device.
When you deserialize a Response<Device>, you start with the result field, but then you need to deserialize inner. For this you look at the Deserialize of Device, which is again a Response<Device>, but now you do not have a result field anymore, since you used that up already.
In other words, the Deserialize implementation is recursive without an exit condition.
I understand. What I am trying to do initially is to deserialize different messages into a Result<T> based on their result field: Ok if result == "success", Err otherwise. I don't want the result field in T. Is there a way to do that?
and create some helper method or implement Into/From to turn the response into a Result if necessary. You might be able to use TryFrom with a newtype (struct MyResult<T>(Result<T, String>)), but I'm not sure the end result would look any better.