如何将远程 crate 的枚举序列化和反序列化为数字?

Posted

技术标签:

【中文标题】如何将远程 crate 的枚举序列化和反序列化为数字?【英文标题】:How do I serialize and deserialize a remote crate's enum as a number? 【发布时间】:2020-05-20 21:11:36 【问题描述】:

我一直在尝试使用serde 为Rust 中的serialport crate 设置以下配置,因此我可以直观地在data_bits 的配置中提供7,但它将被反序列化为serialport::DataBits::Seven .不幸的是,当我希望它是数字 (7) 而不是字符串 (seven) 时,它似乎失败了。

测试用例

cargo.toml

[package]
name = "serde_error"
version = "0.1.0"
authors = ["Jason Miller"]
edition = "2018"

[dependencies]
serialport = "3.3.0"
serde =  version = "1.0", features = ["derive"] 
ron = "0.5.1"

以下导致错误:

6:16: Expected identifier

ma​​in.rs

use serde::Deserialize, Serialize;

#[derive(Serialize, Deserialize, Debug)]
#[serde(remote = "serialport::DataBits")]
pub enum DataBitsDef 
    #[serde(rename = "5")]
    Five,
    #[serde(rename = "6")]
    Six,
    #[serde(rename = "7")]
    Seven,
    #[serde(rename = "8")]
    Eight,


fn default_data_bits() -> serialport::DataBits 
    serialport::DataBits::Eight


#[derive(Debug, Serialize, Deserialize)]
pub struct TransceiverSettings 
    pub vid: u16,
    pub pid: u16,
    pub baud_rate: u32,

    #[serde(default = "default_data_bits", with = "DataBitsDef")]
    pub data_bits: serialport::DataBits,


impl Default for TransceiverSettings 
    fn default() -> Self 
        Self 
            vid: 0x2341,
            pid: 0x0043,
            baud_rate: 115_200,

            data_bits: serialport::DataBits::Eight,
        
    


const TRX_CONFIG: &str = "
(
    vid: 0x2341,
    pid: 0x0043, 
    baud_rate: 9600,
    data_bits: 7,
)
";

fn main() 
    match ron::de::from_str::<TransceiverSettings>(&TRX_CONFIG) 
        Err(e) => eprintln!("", e),
        Ok(c) => println!(":?", c),
    

奇怪的是,将7 写成seven 成功并返回:

TransceiverSettings  vid: 9025, pid: 67, baud_rate: 9600, data_bits: Seven 

ma​​in.rs

use serde::Deserialize, Serialize;

#[derive(Serialize, Deserialize, Debug)]
#[serde(remote = "serialport::DataBits")]
pub enum DataBitsDef 
    #[serde(rename = "5")]
    Five,
    #[serde(rename = "6")]
    Six,
    #[serde(rename = "seven")]
    Seven,
    #[serde(rename = "8")]
    Eight,


fn default_data_bits() -> serialport::DataBits 
    serialport::DataBits::Eight


#[derive(Debug, Serialize, Deserialize)]
pub struct TransceiverSettings 
    pub vid: u16,
    pub pid: u16,
    pub baud_rate: u32,

    #[serde(default = "default_data_bits", with = "DataBitsDef")]
    pub data_bits: serialport::DataBits,


impl Default for TransceiverSettings 
    fn default() -> Self 
        Self 
            vid: 0x2341,
            pid: 0x0043,
            baud_rate: 115_200,

            data_bits: serialport::DataBits::Eight,
        
    


const TRX_CONFIG: &str = "
(
    vid: 0x2341,
    pid: 0x0043, 
    baud_rate: 9600,
    data_bits: seven,
)
";

fn main() 
    match ron::de::from_str::<TransceiverSettings>(&TRX_CONFIG) 
        Err(e) => eprintln!("", e),
        Ok(c) => println!(":?", c),
    

serde_repr

serdedocumentation 中的一个给定示例似乎与我的案例相关,但我还没有设法让它与我的设置一起使用。

Serialize enum as numberserde_repr crate 提供了替代派生宏,它们派生相同的序列化和反序列化特征,但委托给类 C 枚举的底层表示。这允许将类似 C 的枚举格式化为整数而不是 JSON 中的字符串

#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug)]
#[repr(u8)]
enum SmallPrime 
    Two = 2,
    Three = 3,
    Five = 5,
    Seven = 7,

【问题讨论】:

【参考方案1】:

在 serde_repr 0.1.5 中支持 #[serde(remote)] 是 not present。您需要提交拉取请求或问题以添加对它的支持。

请遵循How to transform fields during deserialization using Serde? 和How to transform fields during serialization using Serde? 中的建议:

use serde::Deserialize, Serialize;

mod shim 
    use serde::de::Error, Deserialize, Deserializer, Serialize, Serializer;
    use serialport::DataBits;

    pub fn serialize<S>(v: &DataBits, s: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    
        use DataBits::*;

        let v: u8 = match v 
            Five => 5,
            Six => 6,
            Seven => 7,
            Eight => 8,
        ;

        v.serialize(s)
    

    pub fn deserialize<'de, D>(d: D) -> Result<DataBits, D::Error>
    where
        D: Deserializer<'de>,
    
        use DataBits::*;

        match u8::deserialize(d)? 
            5 => Ok(Five),
            6 => Ok(Six),
            7 => Ok(Seven),
            8 => Ok(Eight),
            o => Err(D::Error::custom(format_args!("Invalid value ", o))),
        
    


#[derive(Debug, Serialize, Deserialize)]
pub struct TransceiverSettings 
    pub vid: u16,
    pub pid: u16,
    pub baud_rate: u32,

    #[serde(default = "default_data_bits", with = "shim")]
    pub data_bits: serialport::DataBits,

【讨论】:

这是一个绝妙的解决方案!我看过serialize_withdeserialize_with,直到现在我都无法理解它。我现在甚至可以为baud_rate 扩展它,只有选择的值是有效的。谢谢!

以上是关于如何将远程 crate 的枚举序列化和反序列化为数字?的主要内容,如果未能解决你的问题,请参考以下文章

Jackson:将枚举值序列化和反序列化为整数

枚举、单例和反序列化

使用 Gson 序列化和反序列化枚举 [重复]

根据另一个字段值序列化和反序列化一个字段

unity含枚举类型json数据的序列化和反序列化

unity含枚举类型json数据的序列化和反序列化