前言
因为最近对工具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,
}
实现
- 在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 }
-
而在我们代码中,
content和caseName是两种不同的用法,因为content我们能明确的知道是存在的,而caseName是不一定存在的。定义content的相关结构代码,这里是采用的源代码,但是更加明确Defaulttrait的使用方式,。Defaulttrait 用于为类型提供一个默认值。当你想要为某个类型提供一个通用的默认实例时,可以实现这个 trait。Defaulttrait 对于需要初始化类型但不需要特定初始值的场景非常有用,例如在使用标准容器或其他泛型数据结构时。
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![],
}
}
}
- 定义与老版本冲突并且不存在的
caseName,这里需要为它指定一个方法来创建默认值,并且如果是新版的数据与老版本的数据合并时重新写入,会覆盖之前的,将所有数据都变成新版本的格式
...
#[serde(default="default_case_name")]
caseName: String,
...
// 这个函数返回你希望在反序列化时使用的默认字符串
fn default_case_name() -> String {
"旧记录".to_string()
}
- 进行数据的读取与写入
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)
}