快速学习Serde包实现rust对象序列化

在处理HTTP请求时,我们总是需要在数据结构对象(可以是enum、struct等)和序列化数据格式(例如JSON,用与存储或传输,并可以反序列化的格式)之间来回转换。

Serde是一个库(crate),用于高效、通用地序列化和反序列化Rust数据结构。在本文中,我将向您展示如何使用Attributes自定义Serde派生生成的序列化和反序列化实现。

在这里插入图片描述

入门示例

  • 增加依赖

    在项目目录中执行命令:

    cargo add serde --features derive
    cargo add serde_json
    

cargo add 命令自动增加指定包至Cargo.toml文件。serde包使用--features derive 标识,启用强大的派生特性;serde_json包处理Json序列化和反序列化。执行命令之后,Cargo.toml文件被更新:

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

让我们从一个简单的结构体Student开始,它的定义如下所示,并初始化我们的第一个学生tom。

use serde::{Serialize, Deserialize};#[derive(Serialize, Deserialize)]
struct Person {name: String,age: u32,email: String,
}fn main() {let person = Person {name: "John Doe".to_string(),age: 30,email: "john@example.com".to_string(),};let json = serde_json::to_string(&person).unwrap();println!("{}", json);
}

输出将如下所示:

{"name":"John Doe","age":30,"email":"john@example.com"
}

看起来太棒了!但是,实际应用中,生成json需要适用不同场景,如重新命名字段、忽略部分字段、拉平嵌套对象等。下面分别进行举例说明。

命名规范

例如,我们实际上希望使用studentId而不是student_id作为字段名。

  • 字段重命名

使用rename对单个字段进行重命名:

struct Student {pub name: String, #[serde(rename="studentId")pub student_id: String,
}
  • 命名规范

使用rename_all 实现整个struct遵循骆驼命名规则:

#[serde(rename_all = "camelCase")]
struct Student {pub name: String, pub student_id: String,
}

除了camelCase之外,您还可以应用其他的case惯例。取值为小写、大写、PascalCase、camelCase、snake_case、SCREAMING_SNAKE_CASE、kebab-case、scream - kebab-case。

您可能想知道的另一件事是,为什么要重命名字段呢?如果所需的字段名是一个保留的Rust关键字类型,那么它是非常有用的。另一个有用的地方是当您使用enum并且希望使用特定名称在外部标记它时。我们很快就会讲到这个。

忽略字段

Skip可用于不希望序列化或反序列化的字段。下面是一个简单的例子。让我们给Student添加birth_year和age。

struct Student {pub name: String, pub student_id: String,pub birth_year: u32,pub age: u32,
}

我们可能希望动态更新年龄,因此需要对学生的birth_year的引用。但是,当我们发送请求时,应该只显示age字段。这可以使用#[serde(skip)]来解决。

struct Student {pub name: String, pub student_id: String,#[serde(skip)]pub birth_year: u32,pub age: u32,
}

配置后,输出的json对象为:

{"name": "tom","studentId": "J19990","age": 123
}

其中birth_year字段被跳过。最常见的两种使用场景是那些Option字段和空vector。

  • Option

假设员工结构有化名alias_name: Option字段。如果我们想在员工没有这个字段的情况下跳过这个字段,我们可以这样做。

#[serde(skip_serializing_if="Option::is_none")]
pub alias_name: Option<String>

输出json,分为两种情形:

// without alias name
{"name": "tom","studentId": "J19990",
}// with alias name
{"name": "tom","studentId": "J19990","aliasName": "middle"
}
  • vector字段

例如,我们为employee结构体提供了projects: Vec字段。由于员工不是必须负责项目,它可以是一个空向量。

要跳过对空向量的序列化,可以向字段添加以下属性。

#[serde(skip_serializing_if="Vec::is_empty")]
pub projects: Vec<String>,

是否配置忽略字段属性的输出差异,根据请求主体的需求,我们可以在两者之间进行选择。

// 未配置属性skip_serializing_if
{"name": "tom","empId": "J19990","projects": []
}// 增加skip_serializing_if属性配置
{"name": "tom","empId": "J19990"
}

扁平化

当你想结构体的一些字段公开和/或给它们默认值,..Default::default()方式特别有用的。下面示例创建名为SideInfo的新结构体,并将Student结构体更改为以下内容。

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Student {pub name: String, pub student_id: String,#[serde(skip_serializing_if="Option::is_none")]pub side_info: Option<SideInfo>
}#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
struct SideInfo {#[serde(skip_serializing_if="Option::is_none")]pub pets: Option<Vec<String>>,#[serde(skip_serializing_if="Option::is_none")]pub address: Option<String>,
}

为了让SideInfo中address字段有值,其他字段未默认值。可以采用..Default::default()方式创建对象:

let student = Student{name:"dan".to_owned(), student_id: "1".to_owned(), side_info:Some(SideInfo{address:Some("47 street".to_owned()), ..Default::default()})
};

输出json如下:

{"name": "dan","studentId": "1","sideInfo": {"address": "47 street"}
}

从输出看到,地址字段嵌套在sideInfo中。但是,通过将属性flatten添加到Student结构中的sideInfo字段:

  #[serde(skip_serializing_if="Option::is_none", flatten)]pub side_info: Option<SideInfo>

输出json:

{"name": "dan","studentId": "1","address": "47 street"
}

反序列化

除了序列化之外,Serde还简化了将JSON字符串反序列化回Rust结构的过程。反序列化允许我们解析JSON数据并将其转换为有意义的Rust对象。

使用我们的Person结构的例子,我们可以有这样的JSON表示,并通过serde_json::from_str方法进行反序列化:

let json_person = r#"
{"name": "Jane Smith","age": 28,"email": "jane@example.com"
}
"#;let person: Person = serde_json::from_str(json_person).unwrap();
println!("Name: {}", person.name);
println!("Age: {}", person.age);
println!("Email: {}", person.email);

输出结果:

Name: Jane Smith
Age: 28
Email: jane@example.com

错误处理

在大多数现实场景中,您将处理通常不可预测或不正确的数据。Serde允许在反序列化过程中进行错误处理:

#[derive(Serialize, Deserialize, Debug)]
struct User {name: String,age: u8,email: String,
}fn main() {let data = r#"{"name": "John Doe","age": "twenty","email": "john@example.com"}"#;let result: Result<User, _> = serde_json::from_str(data);match result {Ok(_) => println!("Successfully deserialized data"),Err(err) => {println!("We ran into an error: {}", err);match err.classify() {serde_json::error::Category::Io => println!("Problem reading file"),serde_json::error::Category::Syntax => println!("Problem with JSON syntax"),serde_json::error::Category::Data => println!("Problem with data"),serde_json::error::Category::Eof => println!("Unexpected end of file"),}}}
}

在上面的示例中,age字段不正确(它需要数字,但我们输入了字符串)。在Error:: classifier函数的帮助下,可以对错误进行分类并决定如何处理它。

常见问题

  1. serde支持自定义序列化?

    回答:支持,serde提供自定义序列化和反序列方式

  2. serde支持那些数据格式?

    回答:serde支持很多数据格式,包括:JSON, YAML, MessagePack, 和BSON等。

  3. serde中**serde_derive**的作用?

​ 回答:**serde_derive**是过程宏,使用它可以自动给结构体派生SerializeDeserialize 实现。

  1. 对于大数据结构serde性能如何?

    回答:它使用Rust的类型系统来减少需要运行时工作量,因此性能和效率非常高。

最后总结

本文介绍了Rust中serde包较为全面的使用指南,可以帮助你更好理解如何使用Serde实现序列化。它是一个高度通用和强大的库,是Rust程序开发中必不可少的工具包。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/469860.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OLED 显示画面的变换操作——上下、左右翻转

OLED 画面旋转 OLED 写入函数定义 OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常 OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常OLED 显示界面转换函数如下 void OLED_DisplayTurn(u8 i) {if(i0…

由播客转向个人定制的音频频道(1)平台搭建

项目的背景 最近开始听喜马拉雅播客的内容&#xff0c;但是发现许多不方便的地方。 休息的时候收听喜马拉雅&#xff0c;但是还需要不断地选择喜马拉雅的内容&#xff0c;比较麻烦&#xff0c;而且黑灯操作反而伤眼睛。 喜马拉雅为代表的播客平台都是VOD 形式的&#xff0…

luckfox-pico-max学习记录

0.文件编译及烧录 SDK包在文件夹/home/tao/linux/luckfox/luckfox-pico-spi应用程序样例在文件夹/home/tao/linux/luckfox-pico-spi/demo编译&#xff1a;sudo ./build.sh生成的镜像文件在./luckfox-pico-spi/output/image中&#xff0c;将所有文件复制到windows电脑文件夹I:\…

一文了解珈和科技在农业遥感领域的服务内容和能力

2020年&#xff0c;农业农村部、中央网信办联合印发了《数字农业农村发展规划&#xff08;2019-2025年&#xff09;》&#xff0c;对数字农业农村建设作出了具体部署。其中&#xff0c;农业遥感作为推进数字农业农村的重要力量贯穿《规划》始终。 今年10月&#xff0c;农业农村…

羊城杯2020Easyphp

审题 看到url&#xff0c;可以想到伪协议读取 尝试过后可以发现&#xff0c;题目绕过了read后面的编码 我们可以尝试双重urlencode进行绕过 ?filephp://filter/read%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%…

【时间之外】IT人求职和创业应知【34】-人和机器人,机器人更可靠

目录 新闻一&#xff1a;人形机器人产业持续高速增长&#xff0c;2026年中国市场规模将突破200亿元 新闻二&#xff1a;AI技术驱动设备厂商格局变化&#xff0c;部分厂商市占率快速提升 新闻三&#xff1a;华为与江淮汽车携手打造超高端品牌“尊界”&#xff0c;计划于明年春…

Linux——基础指令2 + 权限

目录 1.zip/unzip 2.tar 3.bc 4.uname –r 5.重要的几个热键 6.扩展命令 7.shell命令以及运行原理 8.Linux权限的理解 关于权限的三个问题&#xff1a; 1.目录权限 2.缺省权限 3.粘滞位 1.zip/unzip 打包、压缩&#xff1a;使用特定的算法&#xff0c;文件进行合…

pgsql和mysql的自增主键差异

1. 当有历史数据存在时&#xff0c; mysql的自增主键是默认从最大值自增。 pgsql的自增主键取初始值开始逐个尝试&#xff0c;所以存在可能与历史数据的主键重复的情况。 pgsql解决上述问题的方式&#xff1a;重设自增值。 SELECT SETVAL(t_db_filed_id_seq, (SELECT MAX(&q…

【Linux】基础IO及文件描述符相关内容详细梳理

0. C语言文件I/O 在C语言中&#xff0c;我们学习了相关函数来读写文件&#xff0c;例如&#xff1a;fopen&#xff0c;fwrite&#xff0c;fread&#xff0c;fprintf等&#xff0c; 在C语言中文件的打开方式&#xff1a; r Open text file for reading. …

大语言模型在序列推荐中的应用

一、简介 序列推荐技术通过分析用户的过往交互历史&#xff0c;能够有效挖掘出用户可能感兴趣的项目&#xff0c;对于提升各类应用的服务质量具有重要作用。近期&#xff0c;大语言模型&#xff08;LLMs&#xff09;的发展在应对复杂的推荐问题上展现出了显著的优势。不过&…

JavaScript——函数、事件与BOM对象

一、系统函数(JS中预置的函数) JS的预置函数在遇到非数字字符时会停止解析 parseInt 转整型 parseFloat 转浮点型 isNaN !isNaN("10") 检测是否纯数字 eval 把字符串转成算式并计算 1.parseInt(string, radix); 语法&#xff1a; string&#x…

Python酷库之旅-第三方库Pandas(208)

目录 一、用法精讲 971、pandas.MultiIndex.set_levels方法 971-1、语法 971-2、参数 971-3、功能 971-4、返回值 971-5、说明 971-6、用法 971-6-1、数据准备 971-6-2、代码示例 971-6-3、结果输出 972、pandas.MultiIndex.from_arrays类方法 972-1、语法 972-2…

相亲小程序(源码+文档+部署+讲解)

最近我在挖掘一些优秀的开源项目时&#xff0c;无意间发现了一个相当给力的系统——相亲小程序管理系统。这个系统不仅功能实用&#xff0c;而且代码结构清晰&#xff0c;易于二次开发。作为一名技术爱好者&#xff0c;我觉得有必要把这个好东西推荐给我的读者们。接下来&#…

spring cloud 入门笔记1(RestTemplate,Consul)

最大感受&#xff1a; spring cloud无非是将spring boot中的各个工作模块拆分成独立的小spring boot&#xff0c;各个模块之间&#xff0c;不再是通过导包什么的&#xff0c;调用而是通过网路进行各个模块之间的调用 工具一&#xff1a;RestTemplate 在Java代码中发送HTTP请…

高性能分布式缓存Redis-高可用部署

一、主从架构搭建 为什么要进行主从架构搭建&#xff0c;一台redis不行吗&#xff1f; ①、持久化后的数据只在一台机器上&#xff0c;因此当硬件发生故障时&#xff0c;比如主板或CPU坏了&#xff0c;这时候无法重启服务器&#xff0c;有什么办法可以保证服务器发生故障时数…

新的恶意软件活动通过游戏应用程序瞄准 Windows 用户

一种新的恶意软件 Winos4.0 被积极用于网络攻击活动。FortiGuard实验室发现&#xff0c;这种先进的恶意框架是从臭名昭著的 Gh0strat 演变而来的&#xff0c;配备了模块化组件&#xff0c;可在受感染的设备上进行一系列恶意活动。 这些攻击已在游戏相关应用程序中发现&#xf…

Python教程笔记(1)

Python教程笔记 3.1.1 数字3.1.2 文本3.1.3 列表4.2 for语句4.3 range()函数4.7 match语句4.8 定义函数4.9.1 默认值参数4.9.3 特殊参数4.9.5. 解包实参列表 对官方教程中自我感觉生疏的知识点做个记录&#xff0c;以便后面回顾。 3.1.1 数字 除法运算 (/) 总是返回浮点数。 如…

C++笔记---异常

1. 异常的概念 1.1 异常和错误 异常通常是指在程序运行中动态出现的非正常情况&#xff0c;这些情况往往是可以预见并可以在不停止程序的情况下动态地进行处理的。 错误通常是指那些会导致程序终止的&#xff0c;无法动态处理的非正常情况。例如&#xff0c;越界访问、栈溢出…

【RabbitMQ】08-延迟消息

1. 延迟消息 2. 死信交换机 正常队列不需要接受消息。 Configuration public class NormalQueueConfig {Beanpublic DirectExchange normalExchange() {return new DirectExchange("normal.direct");}Beanpublic Queue normalQueue() {return QueueBuilder.durable(…

软件测试——认识测试

在本篇文章中&#xff0c;我会给大家说明一下几个问题&#xff1a; 什么是测试软件测试和开发的区别优秀的测试人员需要具备的素质 通过这几个问题&#xff0c;带大家了解测试这个岗位。 1. 什么是测试&#xff1f; 在我们的日常生活中就有很多测试的例子&#xff0c;比如我…