Rust如何通过serde实现含enum/struct的可变长度CSV序列化反序列化
解决方案
问题根源
你遇到的报错本质是csv crate和serde内置的枚举序列化规则不匹配你的CSV格式:
- 你的CSV是扁平无表头结构,首列是枚举变体名,后续列直接是变体字段值,属于典型的顺序存储的行格式
- serde内置的三种枚举标签模式(内部/外部/相邻)都是为KV结构(如JSON)设计的,无法直接适配这种无字段名的扁平顺序结构
- 你添加的
#[serde(tag = "type")]属于内部标签模式,要求存在名为type的字段存储变体名,但你的CSV没有表头,serde无法找到对应标签字段,因此反序列化失败
最简实现方案
不需要修改你定义的Record枚举,也不需要额外引入依赖,直接通过csv crate的StringRecord手动按行处理即可,代码实现如下:
1. 反序列化逻辑
fn example() -> Result<(), Box<dyn std::error::Error>> { let mut rdr = csv::ReaderBuilder::new() .has_headers(false) .delimiter(b',') .flexible(true) .double_quote(false) .from_path("./test.csv")?; for result in rdr.records() { let record = result?; // 取首列作为枚举变体标识 let variant = record.get(0).ok_or("缺失枚举变体名")?; let record = match variant { "Message" => Record::Message { timestamp: record.get(1).ok_or("缺失timestamp字段")?.to_string(), content: record.get(2).ok_or("缺失content字段")?.to_string(), }, "Measurement" => Record::Measurement { timestamp: record.get(1).ok_or("缺失timestamp字段")?.to_string(), a: record.get(2).ok_or("缺失a字段")?.parse()?, b: record.get(3).ok_or("缺失b字段")?.parse()?, c: record.get(4).ok_or("缺失c字段")?.parse()?, d: record.get(5).ok_or("缺失d字段")?.parse()?, }, "AnotherMeasurement" => Record::AnotherMeasurement { timestamp: record.get(1).ok_or("缺失timestamp字段")?.to_string(), a: record.get(2).ok_or("缺失a字段")?.parse()?, b: record.get(3).ok_or("缺失b字段")?.parse()?, }, v => return Err(format!("未知枚举变体:{}", v).into()), }; println!("{:?}", record); } Ok(()) }
2. 序列化逻辑
fn write_msg() -> Result<(), Box<dyn std::error::Error>> { let msg = Record::Message { timestamp: String::from("time"), content: String::from("content"), }; let mut wtr = csv::WriterBuilder::new() .has_headers(false) .flexible(true) .double_quote(false) .from_writer(std::io::stdout()); match msg { Record::Message { timestamp, content } => { wtr.write_record(&["Message", ×tamp, &content])?; } Record::Measurement { timestamp, a, b, c, d } => { wtr.write_record(&[ "Measurement", ×tamp, &a.to_string(), &b.to_string(), &c.to_string(), &d.to_string(), ])?; } Record::AnotherMeasurement { timestamp, a, b } => { wtr.write_record(&[ "AnotherMeasurement", ×tamp, &a.to_string(), &b.to_string(), ])?; } } wtr.flush()?; Ok(()) }
这个方案逻辑清晰,没有额外依赖,性能也比通用serde序列化更高,非常适合你的场景。
内容的提问来源于stack exchange,提问作者Lewin Probst
相关产品推荐
相关产品推荐

