Closed NikolaLohinski closed 2 years ago
Here are 2 possible implementations. A conceptually simpler one where we just insert a Tag
if there isn't one already present, but it can't deserialize borrowed values this way:
// [dependencies]
// serde = { version = "1.0.147", features = ["derive"] }
// serde_yaml = "0.9.14"
use serde::{Deserialize, Deserializer};
#[derive(Deserialize, Debug, PartialEq)]
#[serde(remote = "Self")]
enum Enum {
Variant(String),
Other(u32),
Default(String),
}
impl<'de> Deserialize<'de> for Enum {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let tagged_value = match serde_yaml::Value::deserialize(deserializer)? {
serde_yaml::Value::Tagged(tagged_value) => *tagged_value,
value => serde_yaml::value::TaggedValue {
tag: serde_yaml::value::Tag::new("Default"),
value,
},
};
Enum::deserialize(tagged_value).map_err(serde::de::Error::custom)
}
}
or a slightly more verbose one that avoids allocation and supports borrowing:
use serde::de::value::{BorrowedStrDeserializer, EnumAccessDeserializer};
use serde::de::{Deserializer, EnumAccess, Visitor};
use serde::Deserialize;
use std::fmt;
use std::marker::PhantomData;
#[derive(Deserialize, Debug, PartialEq)]
#[serde(remote = "Self")]
enum Enum<'a> {
Variant(&'a str),
Other(u32),
Default(&'a str),
}
impl<'de: 'a, 'a> Deserialize<'de> for Enum<'a> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct EnumVisitor<'de>(PhantomData<fn() -> Enum<'de>>);
impl<'de> Visitor<'de> for EnumVisitor<'de> {
type Value = Enum<'de>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("enum Enum")
}
fn visit_borrowed_str<E>(self, s: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let de = BorrowedStrDeserializer::new(s);
Deserialize::deserialize(de).map(Enum::Default)
}
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
where
A: EnumAccess<'de>,
{
let de = EnumAccessDeserializer::new(data);
Enum::deserialize(de)
}
}
deserializer.deserialize_any(EnumVisitor(PhantomData))
}
}
Thanks a lot, I got it working just fine for my use case with your instructions!
As I needed to do serialization as well I had to add to explicitly re-implement Serialize
as follows
impl Serialize for Enum {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer
{
Enum::serialize(self, serializer)
}
}
since adding #[serde(remote = "Self")]
requires defining it in order to get back the default serialization behaviour.
Anyhow, thanks for the help ! 🙏
Hi and thanks for the lib, especially the latest work on YAML tags which it makes it really useable!
I am trying to figure out if there is a way to have a « default » tag applied when none is defined but the expected target type is an enum wanting a tag to be defined.
In code, is there anyway to have this passing ?
I basically want the YAML to be deserialized into an enum, where only some tags are specified, and the others “default” to one of the variants.
Thanks in advance !