如何只对 "stringy" 序列化使用自定义序列化?

How to just use custom serialisation for "stringy" serialisation?

我最近必须掌握自定义 serialisation/deserialisation:

我只想对 JSON 和 RON 使用此自定义“stringy”序列化(和 des.),而对所有二进制序列化(例如 bincode)使用 #[derive(Serialisation, ...。 (将两个字节的 (100, 200) 膨胀为七个或更多字节的 "100:200" 是毫无意义的浪费。)

我需要在单个可执行文件中执行此操作,因为 server/server 通信将是 bincode 或 protobufs,而 client/server 通信将是 JSON.

server/server 和 client/server 通信都将使用相同的可序列化结构。即我想要所有通信的一组结构,但它们应该为 JSON/RON 使用自定义序列化,但为 bin/protobufs.

使用派生序列化

我该怎么做?


更新:

这是工作代码,测试通过:

use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{self, Visitor, Unexpected};
use std::fmt;
use std::str::FromStr;
use regex::Regex;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct DerivedIncline {
    rise: u8,
    distance: u8,
}

impl DerivedIncline {
    pub fn new(rise: u8, distance: u8) -> DerivedIncline {
        DerivedIncline {rise, distance}
    }
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct StringyIncline {
    rise: u8,
    distance: u8,
}

impl StringyIncline {
    pub fn new(rise: u8, distance: u8) -> StringyIncline {
        StringyIncline {rise, distance}
    }
}

impl Serialize for StringyIncline {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&format!("{}:{}", self.rise, self.distance))
    }
}

struct StringyInclineVisitor;

impl<'de> Visitor<'de> for StringyInclineVisitor {
    type Value = StringyIncline;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a colon-separated pair of integers between 0 and 255")
    }

    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        let re = Regex::new(r"(\d+):(\d+)").unwrap(); // PERF: move this into a lazy_static!
        if let Some(nums) = re.captures_iter(s).next() {
            if let Ok(rise) = u8::from_str(&nums[1]) { // nums[0] is the whole match, so we must skip that
                if let Ok(distance) = u8::from_str(&nums[2]) {
                    Ok(StringyIncline::new(rise, distance))
                } else {
                    Err(de::Error::invalid_value(Unexpected::Str(s), &self))
                }
            } else {
                Err(de::Error::invalid_value(Unexpected::Str(s), &self))
            }
        } else {
            Err(de::Error::invalid_value(Unexpected::Str(s), &self))
        }
    }

}

impl<'de> Deserialize<'de> for StringyIncline {
    fn deserialize<D>(deserializer: D) -> Result<StringyIncline, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_string(StringyInclineVisitor)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn serialisation() {
        let stringy_incline = StringyIncline::new(4, 3);
        let derived_incline = DerivedIncline::new(4, 3);

        let json = serde_json::to_string(&stringy_incline).unwrap();
        assert_eq!(json, "\"4:3\"");
    
        let bin = bincode::serialize(&derived_incline).unwrap();
        assert_eq!(bin, [4u8, 3u8]);
    }

    #[test]
    fn deserialisation() {
        let json = "\"4:3\"";
        let bin = [4u8, 3u8];

        let deserialised_json: StringyIncline = serde_json::from_str(&json).unwrap();
        let deserialised_bin: DerivedIncline = bincode::deserialize(&bin).unwrap();

        assert_eq!(deserialised_json, StringyIncline::new(4, 3));
        assert_eq!(deserialised_bin, DerivedIncline::new(4, 3));
    }
}

我想要一个单一的 Incline 结构,它在序列化为 JSON 时像 StringlyIncline 或在序列化为 bincode 时像 DerivedIncline。

如果您每晚使用并且愿意打开 specialization 功能,您可以编写一个函数来告诉您通用参数 S 是否为 serde_json::Serializer

trait IsJsonSerializer {
  fn is_json_serializer() -> bool;
}

impl<T> IsJsonSerializer for T {
  default fn is_json_serializer() -> bool {
     false
  }
}

impl<W,F> IsJsonSerializer for &mut serde_json::Serializer<W,F> {
  fn is_json_serializer() -> bool {
     true
  }
}

那你可以写if S::is_json_serializer() {...}。使用这个你的序列化函数可以写成:

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
struct RawIncline {
    rise: u8,
    distance: u8,
}

impl Serialize for Incline {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        if S::is_json_serializer() {
            serializer.serialize_str(&format!("{}:{}", self.rise, self.distance))
        } else {
            RawIncline{rise:self.rise, distance:self.distance}.serialize(serializer)
        }
    }
}

然后您可以执行类似的反序列化操作。

如果没有 specialization 功能,我想不出有什么方法可以让这样的东西工作,所以它现在仅限于每晚 - 但我很想看看它是否有可能以某种方式实现。