【问题标题】:How to just use custom serialisation for "stringy" serialisation?如何仅将自定义序列化用于“字符串”序列化?
【发布时间】:2020-09-12 05:25:33
【问题描述】:

我最近开始处理自定义序列化/反序列化:https://stackoverflow.com/a/63846824/129805

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

我需要在单个可执行文件中执行此操作,因为服务器/服务器通信将是 bincode 或 protobufs,而客户端/服务器通信将是 JSON。

服务器/服务器和客户端/服务器通信都将使用相同的可序列化结构。即,我希望所有通信都使用一组结构,但它们应该对 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。

【问题讨论】:

  • 您能否提供一些您可能想要使用的接口的伪代码?
  • 感谢您的评论。我已经更新了这个问题。我不想要一个“界面”。我想根据数据被序列化的格式使用不同的序列化或反序列化函数。理想情况下,我想derive 序列化非 JSON 情况,但如有必要,我也可以编写这些函数。
  • 我的意思是interface,就像您可能希望最终使用的 API 一样。很抱歉造成混乱。

标签: rust serde


【解决方案1】:

如果您正在使用 nightly 并且愿意打开 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 功能,我想不出办法让这样的东西工作,所以它现在仅限于每晚 - 但我很想看看是否有可能。

【讨论】:

  • 谢谢,这是一个非常有趣的答案,但我无法使用它,直到 specialization 每晚出现。如果我没有得到其他好的答案,我会在下周接受这个。我正在考虑的其他方法是在板条箱上使用功能标志来指定序列化类型(以某种方式导入同一个板条箱两次,每个标志一次),或者在空标志特征上使用 Incline 泛型控制使用哪个序列化。这些都不是很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-24
  • 1970-01-01
相关资源
最近更新 更多