-
Notifications
You must be signed in to change notification settings - Fork 750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optional field determines which variant #1028
Comments
This is something you could do with a custom deserialize implementation. Here is one possible approach. extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
use serde::ser::{self, Serialize, Serializer};
use serde::de::{self, Deserialize, Deserializer};
use serde_json::Value;
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "method", content = "params")]
#[serde(rename_all = "snake_case")]
enum MyRequests {
ItemAtIndex { index: usize },
Length,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "method", content = "params")]
#[serde(rename_all = "snake_case")]
enum MyNotifications {
InsertItem { item: Value, index: usize },
ShrinkToFit,
}
#[derive(Debug)]
enum JsonRpc<N, R> {
Request(usize, R),
Notification(N),
}
impl<N, R> Serialize for JsonRpc<N, R>
where N: Serialize,
R: Serialize
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
match *self {
JsonRpc::Request(id, ref r) => {
let mut v = serde_json::to_value(r).map_err(ser::Error::custom)?;
v["id"] = json!(id);
v.serialize(serializer)
}
JsonRpc::Notification(ref n) => n.serialize(serializer),
}
}
}
impl<'de, N, R> Deserialize<'de> for JsonRpc<N, R>
where N: Deserialize<'de>,
R: Deserialize<'de>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
#[derive(Deserialize)]
struct IdHelper {
id: Option<usize>,
}
let v = Value::deserialize(deserializer)?;
let helper = IdHelper::deserialize(&v).map_err(de::Error::custom)?;
match helper.id {
Some(id) => {
let r = R::deserialize(v).map_err(de::Error::custom)?;
Ok(JsonRpc::Request(id, r))
}
None => {
let n = N::deserialize(v).map_err(de::Error::custom)?;
Ok(JsonRpc::Notification(n))
}
}
}
}
fn main() {
let j = r#"{
"id": 42,
"method": "item_at_index",
"params": {
"index": 5
}
}"#;
type T = JsonRpc<MyNotifications, MyRequests>;
println!("{:?}", serde_json::from_str::<T>(j).unwrap());
} |
@dtolnay Looks good, thank you for taking the time to write out the example. Is there any way to do this that lets me use borrowing? |
You would need to use a different intermediate type (instead of serde_json::Value) that has a lifetime parameter but other than that, the code would be the same. I filed arcnmx/serde-value#15 to support this in serde-value. |
Moved from #119 (comment):
I'm deserializing JSON-RPCs, which alongside
method
andparams
fields, can optionally have anid
field. The presence of theid
field indicates that the RPC is a request, which requires a response. So in JSON, it might look like this, with theid
field optional:Notifications and Requests are represented as distinct types:
And what I'd like to do is to deserialize based on the presence of the
id
field, maybe using something like this:Does this fit within the scope of this issue? Is this something I could currently do with a custom
deserialize
implementation? (It isn't clear to me that I can.)The text was updated successfully, but these errors were encountered: