Closed tomasol closed 1 year ago
I would write this as:
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
mod other_crate {
#[derive(Debug)]
pub enum QualityOfService {
Level0 = 0,
Level1 = 1,
Level2 = 2,
}
}
use crate::other_crate::QualityOfService;
use serde::{Deserialize, Deserializer};
fn deserialize_qos<'de, D>(deserializer: D) -> Result<QualityOfService, D::Error>
where
D: Deserializer<'de>,
{
match u64::deserialize(deserializer)? {
0 => Ok(QualityOfService::Level0),
1 => Ok(QualityOfService::Level1),
2 => Ok(QualityOfService::Level2),
n => Err(serde::de::Error::custom(format_args!(
"invalid qos value: {}, expected 0 through 2",
n
))),
}
}
#[derive(Deserialize, Debug)]
struct Test {
#[serde(deserialize_with = "deserialize_qos")]
qos_external: QualityOfService,
}
fn main() {
serde_json::from_str::<Test>(r#"{"qos_external":1, "qos_internal":2}"#).unwrap();
}
Thanks @dtolnay , the code looks indeed much nicer (and works :)). I did not know about deserialize_with
. Even though it is somehow mentioned in examples, I would like to suggest adding it to the Implementing Deserialize
page as it solves a problem (custom deserialization for external type) and is much easier to read than the visitor pattern.
@dtolnay I've a very similar issue and would like to use a custom visitor. I think I can't use a similar solution as the one mentioned above, as most of the visitor functions return something custom (especially the visit_newtype_struct
one). Is there a way to use a custom visitor together with remote
?
I also tried it without using remote
, but that broke the case for a definition like
pub enum RemoteEnum {
Breaks(Vec<RemoteEnum>),
}
within visit_seq()
due to RemoteEnum
(which is the remote type) not implementing serde::de::Deserialize<'de>
.
I ended up not using remote
. In order to fix the problem inside visit_seq()
I used:
/// We cannot directly implement `serde::Deserializer` for `RemoteEnum` as it is a remote type.
/// Instead wrap it into a newtype struct and implement `serde::Deserialize` for that one.
/// All the deserializer does is calling the our custom visitor which returns a `RemoteEnum` instance,
/// wraps it in `Wrapper` and returns it.
/// Users of this wrapper will then unwrap it again so that they can return the expected `RemoteEnum`
/// instance.
struct Wrapper(RemoteEnum);
impl<'de> Deserialize<'de> for Wrapper {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let deserialized = deserializer.deserialize_any(MyEnumVisitor);
// Better version of Ok(Wrapper(deserialized.unwrap()))
deserialized.map(Wrapper)
}
}
struct MyEnumVisitor;
impl<'de> de::Visitor<'de> for MyEnumVisitor {
type Value = RemoteEnum;
…
fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
where
V: de::SeqAccess<'de>,
{
let mut vec: Vec<Wrapper> = Vec::new();
while let Some(elem) = visitor.next_element()? {
vec.push(elem);
}
let unwrapped = vec.into_iter().map(|Wrapper(enum)| enum).collect();
Ok(Breaks(unwrapped))
}
…
}
I am trying to implement custom deserialization for external type. I am struggling as there are no mentions of Visitor pattern in https://serde.rs/remote-derive.html . My sample code does not take the
QoSExternalVisitor
into account:Please advise and/or point me to the documentation.