【rust 第三方库】serde 序列化反序列化框架

文章目录

  • [1 serde 框架](#1 serde 框架)
  • [2 使用第三方库序列化(serde_yaml)](#2 使用第三方库序列化(serde_yaml))
    • [2.1 序列化](#2.1 序列化)
      • [1 方法](#1 方法)
    • [2.2 反序列化](#2.2 反序列化)
      • [1 简单示例](#1 简单示例)
      • [2 方法](#2 方法)
  • [3 自定义序列化反序列化](#3 自定义序列化反序列化)
    • [3.1 自定义序列化](#3.1 自定义序列化)
    • [3.2 自定义反序列化](#3.2 自定义反序列化)
  • 程序完整代码
  • 参考链接

1 serde 框架

官网:https://serde.rs/

作用:可以将结构体、枚举、向量、哈希表等rust数据转换为各种各式的数据(目前为16种)例如JSONYALMTOML等,以及将这些格式的数据转换为原始的rust数据类型

简介

Serde是主流的rust序列化、反序列化框架。设计上,基于rust的静态类型系统和元编程(宏)的能力,使Serde序列化的执行速度与手写序列化器的速度相同。

使用上及其简单

  • 用户为自己的类型实现SerializeDeserialize特质即可(大多数使用derive宏实现)
  • 序列化提供商,提供SerializerDeserializer特征的实现即可。

Serde数据模型

Serde数据模型是与rust数据结构和数据格式进行交互的API。可以将其视为Serde的类型系统,Serde将rust类型分为29种。

  • 针对需要序列化的类型,用户需要实现serialize:根据rust类型调用参数Serializer上的方法,而Serializer的实现有序列化提供商提供
  • 针对需要反序列化的类型,用户需要实现Deserialize:根据rust类型调用参数Serializer上的方法,传递一个实现了Visitor的类型

29种类型

  • 14 基础类型
    • bool
    • i8, i16, i32, i64, i128
    • u8, u16, u32, u64, u128
    • f32, f64
    • char
  • string
    • 有长度标记的UTF-8 字节数据(不是\0结尾的形式),可能长度为0
    • 在序列化时,所有类型的字符串被同等处理。在反序列化时,有三种方案:transient, 拥有所有权, 和借用。参见《理解反序列化生命周期》,(Serde使用零拷贝技术)
  • byte array -[u8] (字节数组)
    • 与字符串相似,在反序列化期间,字节数组可以是 transient, 拥有所有权, 和借用
  • option
    • None 或者 Value
  • unit (元组)
    • Rust 中 () 的类型,它表示不包含数据的匿名值
  • unit_struct
    • 例如 struct UnitPhantomData<T>,它表示不包含数据的命名值
  • unit_variant
    • 例如 在 enum E { A, B } 中的 E::AE::B
  • newtype_struct
    • 例如 struct Millimeters(u8)
  • newtype_variant
    • 例如 在 enum E { N(u8) } 中的 E::N
  • seq
    • 可变大小的异质序列
    • 例如 Vec<T> 或者 HashSet<T>
    • 序列化时,长度在遍历之前可能是未知的。在反序列化时,通过 查看数据 可以得知长度
    • 注意,像 vec![Value::Bool(true), Value::Char('c')] 之类的同质Rust集合可以序列化为异构Serde seq,在这种情况下,包含Serde bool和Serde char。
  • tuple
    • 大小静态可知的异质序列
    • 例如 (u8,)(String, u64, Vec<T>)[u64; 10]
    • 其长度在反序列化时就已知道,无需查看数据
  • tuple_struct
    • 命名元组,例如 struct Rgb(u8, u8, u8)
  • tuple_variant
    • 例如 在 enum E { T(u8, u8) } 中 的 E::T
  • map
    • 大小可变的异类键值对,例如 BTreeMap <K, V>。进行序列化时,在遍历所有条目之前,长度可能未知,也可能未知。反序列化时,通过 查看数据 可以得知长度
  • struct
    • 静态大小的异构键值对,其中的键是编译时常量字符串,并且在反序列化时无需查看序列化数据即可知道
    • 例如 struct S { r: u8, g: u8, b: u8 }
  • struct_variant
    • 例如 在 enum E { S { r: u8, g: u8, b: u8 } } 中 的 E::S

属性

属性用于使用派生宏的一些配置,主要分为三类:

  • 容器属性Container attributes:应用在枚举结构体上
  • 变体属性Variant attributes:应用在枚举变体
  • 字段属性Field attributes:应用在结构体枚举变体的 字段上

1)容器属性

  • #[serde(rename_all = "...")]

    • 根据给定的大小写约定重命名所有字段(结构)或 variants(枚举)。"..." 的可选值为 "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE", "kebab-case", "SCREAMING-KEBAB-CASE"
      • kebab-case:短横线命名法
  • #[serde(deny_unknown_fields)]

    • 指定遇到未知字段时,在反序列化期间始终出错。
    • 默认情况下,对于诸如JSON之类的自描述格式,未知字段将被忽略

2)变体属性

  • #[serde(alias = "name")]
    • 反序列化时,对应的别名
    • 允许配置多个
  • #[serde(skip)]
    • 跳过序列化或反序列化此 variant
    • 尝试序列化时将报错
    • 尝试反序列化时将报错

3)字段属性

  • #[serde(alias = "name")]
    • 反序列化时,对应的别名
    • 允许配置多个
  • #[serde(skip)]
    • 跳过此字段:不序列化或反序列化
    • 反序列化时,Serde将使用 Default::default()default = "..." 生成该值

2 使用第三方库序列化(serde_yaml)

2.1 序列化

1 方法

  1. to_string

    • 原型

      rust 复制代码
      pub fn to_string<T>(value: &T) -> Result<String, Error> 
      where
          T: ?Sized + Serialize, 
    • 功能:将给定的数据结构序列化为 YAML 字符串。

    • 示例

      rust 复制代码
      fn to_string() -> Result<(), Box<dyn std::error::Error>>{
          let people = People{
              name:"Alice".to_string(),
              age:30,
              email:"123456".to_string(),
          };
      
          let yaml_string = serde_yaml::to_string(&people)?;
      
          println!("{}", yaml_string);
      
          Ok(())
      }
  2. to_value

    • 原型

      rust 复制代码
      pub fn to_value<T>(value: T) -> Result<Value, Error> 
      where
          T: Serialize, 
    • 功能:将 转换Tserde_yaml::Value可以表示任何有效 YAML 数据的枚举。

      如果想要一个 Value 而不是字符串,你可以使用 serde_yaml::ser::Serializer 来手动序列化你的结构体到 Value。但这样做通常比直接序列化到字符串更加复杂和低效。

    • 示例

      rsut 复制代码
  3. to_writer

    • 原型

      rust 复制代码
      pub fn to_writer<W, T>(writer: W, value: &T) -> Result<(), Error> 
      where
          W: Write,
          T: ?Sized + Serialize, 
    • 功能:将给定的数据结构作为 YAML 序列化到 IO 流中。

    • 示例

      rust 复制代码
      fn to_writer() {
          let people = People{
              name:"Alice".to_string(),
              age:2,
              email:"123456".to_string(),
          };
      
          let mut out = Vec::new(); 
          serde_yaml::to_writer(&mut out, &people).unwrap(); 
          let yaml_write = out.clone();
          fs::write("/home/wangm/rust_exercise/serde_learn/config.yaml", yaml_write).unwrap();
      
          let yaml_str = String::from_utf8(out).unwrap();  
          println!("{}", yaml_str);  
      }

2.2 反序列化

1 简单示例

rust 复制代码
#[derive(Debug,Deserialize)]
pub struct People {
    name: String,
    age: u8,
    email:String, 
}


fn main() {
     // 反序列化
     let json2 = r#"
     {
         name: wangmeng,
         age: 2,
         email: 123456789
     }
     "#;

    let r2: People = serde_yaml::from_str(json2).unwrap();
    println!("People = {:#?}", r2);
    println!("people name: {}", r2.name);
    println!("people age: {}", r2.age);
    println!("people email: {}", r2.email);
}

执行结果:

bash 复制代码
People = People {
    name: "wangmeng",
    age: 2,
    email: "123456789",
}
people name: wangmeng
people age: 2
people email: 123456789

2 方法

  1. from_reader

    • 函数原型

      rust 复制代码
      pub fn from_reader<R, T>(rdr: R) -> Result<T, Error> 
      where
          R: Read,
          T: DeserializeOwned, 
    • 功能:从yaml的io流中反序列化类型示例

    • 示例

      rust 复制代码
      use std::fs::File;
      use std::io::Read;
      use std::fmt;
      use serde::Deserialize;
      use serde::ser::{Serialize, SerializeStruct, Serializer};
      use serde::de::{self,  Deserializer, Visitor};
      
      fn print(people:People) {
          println!("{:#?}", people);
          println!("people name: {}", people.name);
          println!("people age: {}", people.age);
          println!("people email: {}", people.email);
      }
      
      fn from_reader() -> Result<(), Box<dyn std::error::Error>> {
          println!("this is a from_reader test");
          let file = File::open("/home/wangm/rust_exercise/serde_learn/config.yaml")?;
          let people:People = serde_yaml::from_reader(file)?;
      
          print(people);
          Ok(())
      }
      
      fn main() {
           // 反序列化
          let _a = from_reader();
          // let file = File::open("/home/wangm/rust_exercise/serde_learn/config.yaml")?;
          // let people:People = serde_yaml::from_reader(file)?;
      
          // println!("{:#?}", people);
          // Ok(())
      }

      config.yaml

      yaml 复制代码
      name: Alice  
      age: 30  
      email: alice@example.com

      在改程序中,直接打开文件即可,将文件句柄传递给反序列化函数

  2. from_slice

    • 原型

      rust 复制代码
      pub fn from_slice<'de, T>(v: &'de [u8]) -> Result<T, Error> 
      where
          T: Deserialize<'de>, 
    • 功能:从字节反序列化

    • 示例

      rust 复制代码
      fn from_slice() {
          let file = "/home/wangm/rust_exercise/serde_learn/config.yaml";
          
          match fs::read(file) {
              Ok(f) => {
                  let yaml_data = f.as_slice();
                  let people: People = match serde_yaml::from_slice(yaml_data) {
                      Ok(u) => u,
                      Err(e) => {
                          println!("Error parsing YAML: {}", e);
                          return;
                      }
                  };
                  print(people);
              }
              Err(_e) => {
                  println!("file open false");
              }
          }   
      }
  3. from_value

    • 原型

      rust 复制代码
      pub fn from_value<T>(value: Value) -> Result<T, Error> 
      where
          T: DeserializeOwned, 
    • 功能:在不知道要反序列化内容的具体类型时使用,如果你不确定 YAML 文档的确切结构,或者它可能包含多种不同的数据结构,你可以先将其解析为 serde_yaml::Value,然后根据需要动态地决定如何进一步处理它。

  4. from_str

    • 原型

      rust 复制代码
      pub fn from_str<'de, T>(s: &'de str) -> Result<T, Error> 
      where
          T: Deserialize<'de>, 
    • 功能:从 YAML 文本字符串反序列化类型实例

    • 示例

      ./poem.txt内容

      yaml 复制代码
      # == Classic ==
      # This is a shorthand to override some of the options to be backwards compatible
      # with `ls`. It affects the "color"->"when", "sorting"->"dir-grouping", "date"
      # and "icons"->"when" options.
      # Possible values: false, true
      classic: 12
      
      # == Blocks ==
      # This specifies the columns and their order when using the long and the tree
      # layout.
      # Possible values: permission, user, group, context, size, date, name, inode, git
      blocks: test1233

      cargo.toml

      rust 复制代码
      [package]
      name = "all_test"
      version = "0.1.0"
      edition = "2021"
      
      # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
      
      [dependencies]
      serde_yaml = "0.9"		//这个库是用来解析yaml文件的
      serde = { version = "1.0", features = ["derive"] }		// 这是一个序列化和反序列化的框架

      main.rs

      rust 复制代码
      use std::{fs, string};
      use std::path::{Path, PathBuf};
      use serde::{Serialize, Deserialize};
      
      //定义结构体,yaml解析的内容是结构体
      #[derive(Serialize, Deserialize, PartialEq, Debug)]	  // 添加序列化和反序列化的属性
      pub struct Config {
          pub classic: i32,
          pub blocks: String,
      }
      
      fn main() -> Result<(), serde_yaml::Error>	//不带返回值的还没有测试过
      {
          let file = "./poem.txt";
          match fs::read(file) {		//读取文件
              Ok(f) => {
                  let info = String::from_utf8_lossy(&f);			//获取文件内容,去除无效字符
                  let yaml:Config = serde_yaml::from_str(&info)?;	//解析yaml
                  println!("{:?}",yaml);
                  Ok(())
              },
              Err(_e) => {
                  println!("file read false");
                  Ok(())
              },
              
          }
      }

      执行结果:

      bash 复制代码
      Config { classic: 12, blocks: "test1233" }

3 自定义序列化反序列化

若要数据类型支持序列化和反序列化,则该类型需要实现SerializeDeserializetrait。

Serde提供了rust基础类型和标准库类型的SerializeDeserialize实现。对于自定义类型,可以自行实现SerializeDeserializetrait。另外,serde提供一个宏serde_derive来自动为结构体类型和枚举类型生成SerializeDeserialize。该特性需要rust编译器版本在1.31及以上,并且在cargo.toml文件配置serde依赖时,需要features指定该特性。例如:

toml 复制代码
[dependencies]
serde = { version = "1.0", features = ["derive"] }

在代码中引用并使用:

rust 复制代码
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

SerializerDeserializer由第三方crate提供,例如serde_json,serde_yaml等。

3.1 自定义序列化

1)代码

rust 复制代码
use serde::ser::{Serialize,SerializeStruct,};		// 序列化

// #[derive(Serialize)] // 用这种方式
// 普通结构体 步骤如下
//   1. serilaize_struct
//   2. serialize_field
//   3. end
struct Color{
    r:u8,
    g:u8,
    b:u8,
}

// 一般不会用这种方式
impl Serialize for Color {
    fn serialize<S>(&self, serializer:S) -> Result<S:OK, S:Error>
    where 
    	S:Serializer,
    {
        let mut state = serializer.serialize_struct("Color", 3)?;	// 开始序列化一个结构体
        state.serialize_field("r", &self.r)?;	// 序列化一个结构体域
        state.serialize_field("g", &self.g)?;
        state.serialize_field("b", &self.b)?;
        state.end();  // 结束序列化
    }
}

3.2 自定义反序列化

结构体的反序列化比枚举的反序列化更复杂。枚举反序列化是直接根据类型反序列化即可,结构体的反序列化需要将反序列化的内容先变为枚举类型,在继续反序列化。

自定义反序列化器的情况:

  • 非标准格式:当需要处理的数据不是标准的json、yaml、toml等时
  • 复杂的数据结构:它不能直接映射到标准格式中的表示方法
  • 性能优化:
  • 自定义验证和错误处理:标准的序列化/反序列化库可能只提供基本的验证和错误处理机制。有时,可能需要更复杂的验证逻辑或更详细的错误报告。
  • 向前/向后兼容:应用程序需要与旧版本的数据进行交互,或者需要支持不同版本的数据格

1)框架

实现Deserialize trait 来告诉serde xxx类型支持反序列化

rust 复制代码
impl Deserialize for struct_xxx {
    fn deserialize<D>(deserializer:D) -> Result<Self, D::Error>
    where
    	D:Deserializer<'de>,
    {
        // 各种deserializer_xxx 函数进行反序列化
        deserializer.deserializer_xxx();
    }
}

遍历访问者

rust 复制代码
// 自定义反序列化器
struct xxxVisitor;

impl<'de> Visitor<'de> for xxxVisitor {
    type Value = xxx;
    
    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                
            }
    fn visit_xxx();

}

2)代码

rust 复制代码
use serde::{Deserialize, Deserializer,};
  
#[derive(Debug)]  
enum Age {  
    Numeric(u64),  
    // Numeric,
    Child,  
}  
  
#[derive(Deserialize, Debug)]  
struct Person {  
    name: String,  
    age: Age,  
    hobbies: Vec<String>,  
}  
  
// 由于枚举可能包含不同的JSON表示形式,您可能需要提供一个自定义的反序列化函数  
// 例如,如果您希望"age"字段可以是一个数字或一个字符串(如"child")  
impl<'de> Deserialize<'de> for Age {  
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>  
    where  
        D: serde::Deserializer<'de>,  
    {  
        struct AgeVisitor;  
  
        impl<'de> serde::de::Visitor<'de> for AgeVisitor {  
           
            type Value = Age;  
  
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {  
                formatter.write_str("a number or 'child' string")  
            }  
  
            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>     // 默认类型是64 改成u32后解析报错
            where  
                E: serde::de::Error,  
            {  
                Ok(Age::Numeric(value))  
            }  
  
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>  
            where  
                E: serde::de::Error,  
            {  
                if value == "child" {  
                    Ok(Age::Child)  
                } else {  
                    Err(serde::de::Error::custom(format!("invalid age: {}", value)))  
                }  
            }  
  
            // 如果需要支持其他类型,可以添加更多的visit方法  
        }  
  
        deserializer.deserialize_any(AgeVisitor)  
    }  
}  
  
pub fn custom_deserialize() {  
    let json = r#"  
    {  
        "name": "Alice",  
        "age": "child", 
        "hobbies": ["reading", "swimming"] 
    }  
    "#;  
      let json2 = r#"  
     {  
         "name": "Alice",  
         "age": 8, 
         "hobbies": ["reading", "swimming"] 
     }  
     "#; 
    
    let person: Person = serde_yaml::from_str(json).unwrap();  
    println!("{:#?}", person);  
    
    let person: Person = serde_yaml::from_str(json2).unwrap();  
    println!("{:#?}", person); 
}

程序完整代码

serde_learn

参考链接

[1]https://whiteccinn.github.io/2021/04/13/Rust%E8%AF%AD%E8%A8%80/rust-%E5%BA%8F%E5%88%97%E5%8C%96%E6%A1%86%E6%9E%B6serde/

[2]官方文档

[3]Rust 序列化反序列框架 Serde

相关推荐
简单.is.good15 分钟前
【测试】接口测试与接口自动化
开发语言·python
Yvemil735 分钟前
MQ 架构设计原理与消息中间件详解(二)
开发语言·后端·ruby
程序员是干活的36 分钟前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
2401_854391081 小时前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端
我是陈泽1 小时前
一行 Python 代码能实现什么丧心病狂的功能?圣诞树源代码
开发语言·python·程序员·编程·python教程·python学习·python教学
优雅的小武先生1 小时前
QT中的按钮控件和comboBox控件和spinBox控件无法点击的bug
开发语言·qt·bug
虽千万人 吾往矣1 小时前
golang gorm
开发语言·数据库·后端·tcp/ip·golang
创作小达人1 小时前
家政服务|基于springBoot的家政服务平台设计与实现(附项目源码+论文+数据库)
开发语言·python
郭二哈1 小时前
C++——list
开发语言·c++·list
杨荧1 小时前
【JAVA开源】基于Vue和SpringBoot的洗衣店订单管理系统
java·开发语言·vue.js·spring boot·spring cloud·开源