【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)
    }

参考

相关推荐
百事老饼干8 分钟前
Java[面试题]-真实面试
java·开发语言·面试
杨荧1 小时前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
白子寰1 小时前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
王俊山IT1 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。1 小时前
c++多线程
java·开发语言
小政爱学习!1 小时前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
神奇夜光杯2 小时前
Python酷库之旅-第三方库Pandas(202)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
Themberfue2 小时前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
plmm烟酒僧2 小时前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv