【rust/egui】(十一)使用rfd选择文件并使用serde_json进行序列化

说在前面

  • rust新手,egui没啥找到啥教程,这里自己记录下学习过程
  • 环境:windows11 22H2
  • rust版本:rustc 1.71.1
  • egui版本:0.22.0
  • eframe版本:0.22.0
  • 上一篇:这里

rfd-Rusty File Dialogs

  • 一个跨平台的本地文件选择库,支持的平台:

    • Windows
    • macOS
    • Linux & BSDs (GTK3 or XDG Desktop Portal)
    • WASM32 (async only)
  • 让我们来看看使用:

    rust 复制代码
    if ui.button("open file").clicked() {
    	if let Some(path) = rfd::FileDialog::new().pick_file() {
        	self.picked_path = Some(path.display().to_string());
        }
    }
  • 还可以添加文件后缀筛选:

    rust 复制代码
    if ui.button("open file").clicked() {
    	if let Some(path) = rfd::FileDialog::new().add_filter("text", &["txt", "rs"]).pick_file() {
        	self.picked_path = Some(path.display().to_string());
        }
    }
  • 有了文件路径之后,我们就可以通过标准的文件库进行读写了

serde_json

  • json序列化与反序列化库

  • 在之前的文章中我们已经初步接触了serde相关知识,这里我们来看看其他内容

  • 在我们获取到文件路径后,我们就可以读取json文件了,同时,对于比较大的文件,serde_json也提供了from_reader的方法:

    rust 复制代码
    use serde::Deserialize;
    
    use std::error::Error;
    use std::fs::File;
    use std::io::BufReader;
    use std::path::Path;
    
    #[derive(Deserialize, Debug)]
    struct User {
        fingerprint: String,
        location: String,
    }
    
    fn read_user_from_file<P: AsRef<Path>>(path: P) -> Result<User, Box<dyn Error>> {
        // 使用只读方式读取文件 并使用buffer存储
        let file = File::open(path)?;
        let reader = BufReader::new(file);
    
        // 反序列化json数据
        let u = serde_json::from_reader(reader)?;
    
        // 返回
        Ok(u)
    }
    
    fn main() {
        let u = read_user_from_file("test.json").unwrap();
        println!("{:#?}", u);
    }
  • 在我们的例子中,首先定义下结构体:

    rust 复制代码
    #[derive(serde::Deserialize, serde::Serialize)]
    pub struct WorkSpace {
        pub name: String,
        pub path: String,
        pub description: String,
        pub data: Project,
    }
    
    #[derive(serde::Deserialize, serde::Serialize)]
    pub struct Project {
        version: String,
        scope: String,
        selected_tree: String,
    }
  • 然后是初始化代码:

    rust 复制代码
    impl WorkSpace {
        pub fn new(path: String) -> Self {
             Self::from_file(path).unwrap()
        }
        fn from_file<P: AsRef<Path>>(path: P) -> Result<WorkSpace, Box<dyn Error>> {
            // 使用只读方式读取文件 并使用buffer存储
            let file = File::open(path)?;
            let reader = BufReader::new(file);
    
            let u = serde_json::from_reader(reader)?;
    
            Ok(u)
        }
    }
  • 我们的json数据如下:

    json 复制代码
    {
    	"name": "test",
    	"path": "C:\\Users\\b3.txt",
    	"description": null,
    	"data": {
    		"version": "0.0.1",
    		"scope": "project",
    		"selectedTree": "045b5abc-aef7-4909-8d16-5797ebb270e9",
    	}
    }
  • 运行我们的代码,选择json文件,发现报错了:

    shell 复制代码
    thread 'main' panicked at 
    'called `Result::unwrap()` on an `Err` value: Error("invalid type: null, expected a string", line: xxx, column: 17)',
     src\project.rs:18:32

    这是因为对于WorkSpace.description,我们定义的String类型,但是我们的json数据中却是null,匹配不上,要解决这个问题,我们可以这样:

    rust 复制代码
    #[derive(serde::Deserialize, serde::Serialize)]
    pub struct WorkSpace {
        pub name: String,
        pub path: String,
        pub description: serde_json::Value,
        pub data: Project,
    }

    将description修改为枚举Value

    rust 复制代码
    pub enum Value {
        Null,
        Bool(bool),
        Number(Number),
        String(String),
        Array(Vec<Value>),
        Object(Map<String, Value>),
    }
  • 再次运行代码,发现又报错了:

    shell 复制代码
    thread 'main' panicked at 
    'called `Result::unwrap()` on an `Err` value: Error("missing field `selected_tree`", line: 245515, column: 2)',
    src\project.rs:18:32

    这是因为Project.selected_tree的默认反序列化名称为selected_tree,而在我们的json数据中为selectedTree,这时我们可以这样处理:

    rust 复制代码
    #[derive(serde::Deserialize, serde::Serialize)]
    pub struct Project {
        version: String,
        scope: String,
    
        #[serde(rename(serialize = "selectedTree", deserialize = "selectedTree"))]
        selected_tree: String,
    }

    这样就可以指定序列化与反序列化时的名称为selectedTree

  • 然后我们来试试序列化并保存文件,同样可以使用I/O stream

    rust 复制代码
    pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), serde_json::Error> {
        let file = File::options().create_new(true).write(true).open(path).unwrap();
    
        let writer = BufWriter::new(file);
    
        serde_json::to_writer_pretty(writer, self)
    }

参考

相关推荐
weixin_460443564 小时前
2026年新修订《网络安全法》实施:Java私有化考试系统如何做好SBOM、漏洞治理与安全升级?
java·开发语言·在线考试系统·企业培训考试系统·宏远培训考试系统·国企培训考试系统·国产化培训系统
Cc.Y8 小时前
Java零基础入门:可变字符串与包装类:StringBuilder、StringBuffer 与 通讯录管理系统实战
java·开发语言·python
阿森_dev8 小时前
2027 届毕设选题怎么选:30 个 Java 方向选题的实现难度排序
java·开发语言·spring boot·毕业设计·课程设计
by209998 小时前
C++ 多态:同一个基类接口,调用不同的函数
开发语言·c++·经验分享·多态
笔墨登场说说8 小时前
flink bin/start-cluster.sh 帮我做成开机启动
开发语言·python
Sarvartha8 小时前
Lambda 表达式和函数式接口
java·开发语言
小白学大数据8 小时前
拼多多反爬对抗实战:Scrapy 中间件化采集架构解析
开发语言·scrapy·中间件·架构
学逆向的9 小时前
win32注入代码
开发语言·网络安全·api·win32
Sarvartha9 小时前
内部类知识
java·开发语言
DongQiShanRen9 小时前
玄龙(上):TICK 主循环——意识心跳怎么跳
linux·jvm·数据库·人工智能·数据挖掘·rust