如何只对 "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
功能,我想不出有什么方法可以让这样的东西工作,所以它现在仅限于每晚 - 但我很想看看它是否有可能以某种方式实现。
我最近必须掌握自定义 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
功能,我想不出有什么方法可以让这样的东西工作,所以它现在仅限于每晚 - 但我很想看看它是否有可能以某种方式实现。