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

2024-06-12 02:44

本文主要是介绍【rust 第三方库】serde 序列化反序列化框架,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1 serde 框架
    • 简介
    • Serde数据模型
      • 属性
  • 2 使用第三方库序列化(serde_yaml)
    • 2.1 序列化
      • 1 方法
    • 2.2 反序列化
      • 1 简单示例
      • 2 方法
  • 3 自定义序列化反序列化
    • 3.1 自定义序列化
    • 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

    • 原型

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

    • 示例

      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

    • 原型

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

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

    • 示例

      
      
  3. to_writer

    • 原型

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

    • 示例

      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 简单示例

#[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);
}

执行结果:

People = People {name: "wangmeng",age: 2,email: "123456789",
}
people name: wangmeng
people age: 2
people email: 123456789

2 方法

  1. from_reader

    • 函数原型

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

    • 示例

      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

      name: Alice  
      age: 30  
      email: alice@example.com
      

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

  2. from_slice

    • 原型

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

    • 示例

      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

    • 原型

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

  4. from_str

    • 原型

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

    • 示例

      ./poem.txt内容

      # == 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

      [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

      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)?;	//解析yamlprintln!("{:?}",yaml);Ok(())},Err(_e) => {println!("file read false");Ok(())},}
      }

      执行结果:

      Config { classic: 12, blocks: "test1233" }
      

3 自定义序列化反序列化

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

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

[dependencies]
serde = { version = "1.0", features = ["derive"] }

在代码中引用并使用:

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

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

3.1 自定义序列化

1)代码

use serde::ser::{SerializeSerializeStruct,};		// 序列化// #[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类型支持反序列化

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

遍历访问者

// 自定义反序列化器
struct xxxVisitor;impl<'de> Visitor<'de> for xxxVisitor {type Value = xxx;fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {}fn visit_xxx();}

2)代码

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

这篇关于【rust 第三方库】serde 序列化反序列化框架的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1052978

相关文章

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

TP-Link PDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务

《TP-LinkPDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务》近期,路由器制造巨头普联(TP-Link)在用户群体中引发了一系列重要变动,上个月,公司发出了一则通知,明确要求所... 路由器厂商普联(TP-Link)上个月发布公告要求所有用户必须完成实名认证后才能继续使用普联提供的 D

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

在Rust中要用Struct和Enum组织数据的原因解析

《在Rust中要用Struct和Enum组织数据的原因解析》在Rust中,Struct和Enum是组织数据的核心工具,Struct用于将相关字段封装为单一实体,便于管理和扩展,Enum用于明确定义所有... 目录为什么在Rust中要用Struct和Enum组织数据?一、使用struct组织数据:将相关字段绑

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

Java中JSON字符串反序列化(动态泛型)

《Java中JSON字符串反序列化(动态泛型)》文章讨论了在定时任务中使用反射调用目标对象时处理动态参数的问题,通过将方法参数存储为JSON字符串并进行反序列化,可以实现动态调用,然而,这种方式容易导... 需求:定时任务扫描,反射调用目标对象,但是,方法的传参不是固定的。方案一:将方法参数存成jsON字

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte