【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)
  • 让我们来看看使用:
    if ui.button("open file").clicked() {
    	if let Some(path) = rfd::FileDialog::new().pick_file() {
        	self.picked_path = Some(path.display().to_string());
        }
    }
    
    【rust/egui】(十一)使用rfd选择文件并使用serde_json进行序列化_第1张图片
  • 还可以添加文件后缀筛选:
    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());
        }
    }
    
    【rust/egui】(十一)使用rfd选择文件并使用serde_json进行序列化_第2张图片
  • 有了文件路径之后,我们就可以通过标准的文件库进行读写了

serde_json

  • json序列化与反序列化库

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

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

    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);
    }
    
  • 在我们的例子中,首先定义下结构体:

    #[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,
    }
    
  • 然后是初始化代码:

    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数据如下:

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

    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,匹配不上,要解决这个问题,我们可以这样:

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

    description修改为枚举Value

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

    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,这时我们可以这样处理:

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

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

参考

  • rfd
  • serde_json
  • serde

你可能感兴趣的:(Rust,rust,json,开发语言)