基于某些条件对特定字段使用不同的序列化方法



我正在开发库。它正在编码一个对象

pub struct obj {
pub appliance_id: u32,
pub last: bool,
pub path: PathBuf,
}

而且,以前没有处理无效utf-8的编码。因此,我编写了一个单独的方法来序列化路径

pub fn serialize<S>(p: &Arc<PathBuf>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let b = p.as_os_str().as_bytes();
serializer.serialize_bytes(b)
}

我想要的是,如果路径是有效的utf-8,那么它应该使用PathBuf的默认序列化程序进行序列化,即将其转换为str,然后对其进行序列化。而且,在utf-8无效的情况下,它应该回退到为序列化路径编写的新方法。反序列化程序也应该如此。

首先,我假设您只在cfg(unix)上工作就可以了,因为您使用的是OsStrExt::as_bytes。在一般情况下,访问非utf8PathBufs的内容是很棘手的。

序列化函数主要由@Bamontan:提供

fn serialize<S>(p: &PathBuf, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if let Some(utf8) = p.to_str() {
serializer.serialize_str(utf8)
} else {
serializer.serialize_bytes(p.as_os_str().as_bytes())
}
}

反序列化更为复杂,因为您需要一个既能处理字符串又能处理字节数组的访问者:

fn deserialize<'de, D>(d: D) -> Result<PathBuf, D::Error>
where
D: Deserializer<'de>,
{
struct V;
impl<'de> serde::de::Visitor<'de> for V {
type Value = PathBuf;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a string or array of bytes")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(s.into())
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut res = vec![];
while let Some(b) = seq.next_element::<u8>()? {
res.push(b);
}
Ok(OsString::from_vec(res).into())
}
}
d.deserialize_any(V)
}

Playground
当然,只有当您的序列化格式像JSON一样自我描述时,这才有效,即使用deserialize_any并让serde判断是否有列表或字符串是可以的。如果不是这样,您将需要一个类型标记或类似的标记。

可能有更简单的方法来解决这个问题(首先反序列化为JsonValue并进行转换(,也可能有更优雅的方法(可能反序列化为Either<Vec<u8>, String>并进行转化(,但这应该让你开始。

最新更新