记录一次Rust serde - default的用处

537 阅读3分钟

前言

因为最近对工具Visa Tools的改版,需要增加一个测试项目名称caseName字段,并且将它添加到历史记录中,这样问题就产生了。

在我们的老版本中我们写入历史记录,是不存在caseName的,而且这个记录是储存在临时文件Temp文件中,名字是visa_data_temp.json,结构如下

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TempData {
    name: String,
    content: Content,
}

而在新的版本中,需要添加一个caseName字段,这样就出现老版本与新版本在代码中是不兼容的,这个时候我们就需要去给它添加一个默认值default,结构代码如下

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TempData {
    name: String,
    #[serde(default)]
    content: Content,
    #[serde(default="default_case_name")]
    caseName: String,
}

实现

  1. 在Rust中使用Serde库进行序列或者反序列时,#[serde(default = "default_case_name")] 属性用于指定当数据缺失时使用的默认函数。这对于处理可能缺少某些字段的 JSON 数据或其他格式非常有用,如下提供简单示例
use serde::{Serialize, Deserialize}; 
// 定义一个结构体,包含可能缺失的字段 
#[derive(Serialize, Deserialize, Debug)] 
struct User { 
    name: String, 
    // 当 age 字段在输入数据中缺失时,将使用 default_age 函数提供的默认值 
    #[serde(default = "default_age")] 
    age: u8, 
} 
// 提供一个返回默认年龄的函数 
fn default_age() -> u8 { 30 }
  1. 而在我们代码中,contentcaseName是两种不同的用法,因为content我们能明确的知道是存在的,而caseName是不一定存在的。定义content的相关结构代码,这里是采用的源代码,但是更加明确Default trait的使用方式,。

    Default trait 用于为类型提供一个默认值。当你想要为某个类型提供一个通用的默认实例时,可以实现这个 trait。

    Default trait 对于需要初始化类型但不需要特定初始值的场景非常有用,例如在使用标准容器或其他泛型数据结构时。

use serde::{Deserialize, Serialize};


// 定义数据结构
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Legend {
    data: Vec<String>,
    textStyle: TextStyle,
}
impl Default for Legend {
    fn default() -> Self {
        Self {
            data: vec![],
            textStyle: TextStyle {
                color: String::new(),
            },
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TextStyle {
    color: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Series {
    name: String,
    data: Vec<f64>,
    yAxisIndex: u32,
    tooltip: Tooltip,
}

impl Default for Series {
    fn default() -> Self {
        Series {
            name: "".to_string(),
            data: vec![],
            yAxisIndex: 0,
            tooltip: Tooltip {},
        }
    }
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tooltip {}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Content {
    legend: Legend,
    series: Vec<Series>,
}
impl Default for Content {
    fn default() -> Self {
        Content {
            legend: Legend::default(),
            series: vec![],
        }
    }
}

  1. 定义与老版本冲突并且不存在的caseName,这里需要为它指定一个方法来创建默认值,并且如果是新版的数据与老版本的数据合并时重新写入,会覆盖之前的,将所有数据都变成新版本的格式
    ...
    #[serde(default="default_case_name")]
    caseName: String,
    ...
    
    
// 这个函数返回你希望在反序列化时使用的默认字符串
fn default_case_name() -> String {
    "旧记录".to_string()
}
  1. 进行数据的读取与写入
    pub fn write(&self, data: &TempData) -> io::Result<()> {
        // 读取现有内容
        let mut file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(&self.path)?;

        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        // 反序列化现有内容
        let mut data_vec: Vec<TempData> = if contents.trim().is_empty() {
            vec![]
        } else {
            serde_json::from_str(&contents)?
        };

        // 添加新的数据
        data_vec.push(data.clone());

        // 序列化并写回文件
        let json_data = serde_json::to_string(&data_vec)?;
        file.set_len(0)?; // 清空文件
        file.seek(SeekFrom::Start(0))?; // 将文件指针移动到文件开头
        file.write_all(json_data.as_bytes())?;
        file.flush()?;

        Ok(())
    }
    pub fn read(&self) -> io::Result<Vec<TempData>> {
        let mut file = File::open(&self.path)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let data: Vec<TempData> = serde_json::from_str(&contents)?;
        Ok(data)
    }

文章属于记录,如果有推荐其他的写法或者方案,请评论提供,非常感谢,