如何在Serde序列化字符串(枚举转CSV场景)时避免添加双引号?
问题描述
我试图将一个枚举序列化为CSV的两个连续字段:第一个字段是依枚举变体而定的固定字符串,第二个字段是枚举变体中的值并附加“Hz”后缀。我已为该枚举实现了Display trait以生成预期格式,再使用SerializeDisplay进行序列化,但生成的CSV字段被添加了不必要的双引号。
代码示例:
use serde_with::SerializeDisplay; use std::fmt; #[derive(SerializeDisplay)] pub enum Tone { Up(String), Off, } impl fmt::Display for Tone { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { match self { Tone::Up(s) => write!(f, "TONE,{}Hz", s), Tone::Off => write!(f, "OFF,87.5Hz"), } } } fn main() { let tone = Tone::Up("hi".to_string()); let mut wtr = csv::WriterBuilder::new() .has_headers(false) .from_writer(vec![]); wtr.serialize(tone).unwrap(); let csv = String::from_utf8(wtr.into_inner().unwrap()).unwrap(); assert_eq!(csv, "TONE,hiHz\n"); }
运行错误:
assertion `left == right` failed left: "\"TONE,hiHz\"\n" right: "TONE,hiHz\n"
解决方案
问题根源在于:你通过Display把两个CSV字段拼成了一个带逗号的字符串,而CSV规范中,包含逗号、换行或引号的字段必须用双引号包裹,所以writer自动添加了引号。正确的做法是让枚举直接序列化为两个独立字段,而非拼接成单一字符串。
方法一:手动实现Serialize trait(推荐)
放弃SerializeDisplay,手动实现Serialize,让枚举序列化为两个字段的元组,这样CSV writer会输出标准的双字段内容,无需额外引号。
修改后的代码:
use serde::Serialize; use std::fmt; #[derive(Debug)] pub enum Tone { Up(String), Off, } // 保留Display实现(如果需要) impl fmt::Display for Tone { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Tone::Up(s) => write!(f, "TONE,{}Hz", s), Tone::Off => write!(f, "OFF,87.5Hz"), } } } // 实现Serialize,将枚举转为双字段元组 impl Serialize for Tone { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { let (field1, field2) = match self { Tone::Up(s) => ("TONE", format!("{}Hz", s)), Tone::Off => ("OFF", "87.5Hz".to_string()), }; (field1, field2).serialize(serializer) } } fn main() { let tone = Tone::Up("hi".to_string()); let mut wtr = csv::WriterBuilder::new() .has_headers(false) .from_writer(vec![]); wtr.serialize(tone).unwrap(); let csv = String::from_utf8(wtr.into_inner().unwrap()).unwrap(); assert_eq!(csv, "TONE,hiHz\n"); // 断言通过 }
方法二:强制CSV writer禁用引号(不推荐)
如果一定要沿用Display拼接字符串的方式,可以配置CSV writer永不添加引号,但这会破坏CSV规范——若字段后续出现换行、引号等特殊字符,生成的CSV将无法被正确解析。
修改后的代码:
use serde_with::SerializeDisplay; use std::fmt; #[derive(SerializeDisplay)] pub enum Tone { Up(String), Off, } impl fmt::Display for Tone { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { match self { Tone::Up(s) => write!(f, "TONE,{}Hz", s), Tone::Off => write!(f, "OFF,87.5Hz"), } } } fn main() { let tone = Tone::Up("hi".to_string()); let mut wtr = csv::WriterBuilder::new() .has_headers(false) .quote_style(csv::QuoteStyle::Never) // 强制禁用所有引号 .from_writer(vec![]); wtr.serialize(tone).unwrap(); let csv = String::from_utf8(wtr.into_inner().unwrap()).unwrap(); assert_eq!(csv, "TONE,hiHz\n"); // 断言通过 }
内容的提问来源于stack exchange,提问作者caiman
相关产品推荐
相关产品推荐

