Serde 自定义序列化器

Kev*_*vin 5 rust serde

我正在遵循使用serde. 它提供了使用 Serde 的 JSON 序列化器的基本实现。假设我只想序列化无符号整数u8,u16u32。是否可以以某种方式指定此约束并在用户尝试序列化任何其他类型(例如等)时抛出string错误f32

我也许可以写

fn serialize_string(self, v: string) -> Result<()> {
    return Err(Error::Message("Undefined".to_owned()));
}

fn serialize_f32(self, v: f32) -> Result<()> {
    return Err(Error::Message("Undefined".to_owned()));
}

/* and so forth */
Run Code Online (Sandbox Code Playgroud)

但这感觉太冗长了。

是否可以定义一个用于序列化自定义类型的协议?例如,我可能有一个Point包含两个坐标x和 的结构y。我想Point以压缩格式进行序列化,如下所述: https: //bitcoin.stackexchange.com/a/84588,即将第一个坐标序列化为x任何其他无符号整数,但添加一个小前缀来指示坐标y是偶数还是奇数。然后可以使用前缀(有条件的)派生y出。x

Loc*_*cke 1

不幸的是,实际上没有任何方法可以实现所有未使用的情况。如果您发现它造成太多混乱,您可以尝试为未使用的函数创建一个宏,或者添加带有每个函数的默认实现的包装器特征。

至于序列化自定义类型,有几种方法,但我不确定是否有官方推荐的方法。

不过,实现此目的的一个简单方法是利用Serializer::serialize_newtype_structDeserializer::deserialize_newtype_struct。这些函数通常用于具有单个值的元组结构的情况。但是,它们为序列化器提供了根据名称切换到内部值的新序列化器类型的选项,而无需修改序列化器的任何关联类型。因此,这意味着您可以定义一个不太可能在序列化器之外的任何地方使用的名称,并在出现时切换到不同的序列化器/反序列化器策略。

/// New type name name used to convey to serializer/deserializer that our special point type
/// is being used. String intentionally contains null character to make it highly unlikely
/// that another implementation would use the same type name.
const POINT_INTERNAL_NAME: &'static str = "foo::Point\x08";
Run Code Online (Sandbox Code Playgroud)

然后定义您的特殊类型,宣布它实际上是具有此内部名称的新类型。

/// The type we want to implement a custom serialization approach for
pub struct Point {
    pub x: f32,
    pub y: f32,
}

impl Serialize for Point {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        struct PointProxy {
            x: f32,
            y: f32,
        }

        /// This is exactly the same as what would have been generated if we used 
        /// `#[derive(Serialize)]` on [Point]. We implement this manually in this case to
        /// ensure that the implementation stays stable even if a change is made to
        /// `serde_derive`.
        impl Serialize for PointProxy {
            fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
                let mut struct_serializer = serializer.serialize_struct("Point", 2)?;
                struct_serializer.serialize_field("x", &self.x)?;
                struct_serializer.serialize_field("y", &self.y)?;
                struct_serializer.end()
            }
        }

        let proxy_value = Point {
            x: self.x,
            y: self.y,
        };

        serializer.serialize_newtype_struct(POINT_INTERNAL_NAME, &proxy_value)
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,序列化器选择序列化时使用的方法。

impl Serializer for FooSerializer {
    fn serialize_newtype_struct<T: Serialize + ?Sized>(
        self,
        name: &'static str,
        value: &T
    ) -> Result<Self::Ok, Self::Error> {
        if name == POINT_INTERNAL_NAME {
            return value.serialize(FooSpecialSerializer::new(self));
        }

        // Handle all other cases normally
        value.serialize(self)
    }

    /* etc. */
}
Run Code Online (Sandbox Code Playgroud)

然后,您实现为特定类型定制的特殊序列化器。

struct FooPointSerializer<W> {
    out: W,
    x: Option<f32>,
    y: Option<f32>,
}

impl<W: Write> Serializer for FooPointSerializer<W> {
    type SerializeStruct = Self;

    fn serialize_struct(
        self,
        name: &'static str,
        len: usize
    ) -> Result<Self::SerializeStruct, Self::Error> {
        // Only our Point implementation should be using this serializer
        assert_eq!(name, "Point");
        assert_eq!(len, 2);

        Ok(self)
    }

    /* etc. */
}

impl<W: Write> SerializeStruct  for FooPointSerializer<W> {
    type Ok = ();
    type Error = <FooSerializer as Serializer>::Error;

    // Required methods
    fn serialize_field<T: Serialize + ?Sized>(
        &mut self,
        key: &'static str,
        value: &T
    ) -> Result<(), Self::Error> {
        // How you convert the value to your desired type is up to you. In this case, assume
        // there is a serializer that fills in value upon a call to serialize_f32 and errors
        // in all other cases.
        let mut value: f32 = 0.0;
        value.serialize(ExpectSerializeF32(&mut value))?;

        // Verify assumptions about fields
        match key {
            "x" => {
                assert!(x.is_none(), "Expected 'x' to be first field in Point");
                self.x = Some(value);
            },
            "y" => {
                assert!(x.is_some() && y.is_none(), "Expected 'y' to be second field in Point");
                self.y = Some(value);
            },
            _ => panic!("Unexpected field {:?} while serializing point", key),
        }

        Ok(())
    }


    fn end(mut self) -> Result<Self::Ok, Self::Error> {
        let (Some(x), Some(y)) = (self.x, self.y) else {
             panic!("Point did not serialize expected number of fields");
        }

        // Implement custom serialization for this type
        self.out.write_all(&[POINT_TYPE_TAG])?;
        self.out.write_all(&x.to_be_bytes())?;
        self.out.write_all(&y.to_be_bytes())?;
        Ok(())
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法的好处是,绝大多数序列化程序对新类型结构的名称没有任何意义,因此可以相当安全地假设,如果有人Point在另一个序列化程序(例如serde_json)中使用您的自定义类型,它将以相同的方式序列化会#[derive(Serialize)]的。