【问题标题】:Serialize/Deserialize CSV with nested enum/struct with serde in Rust在 Rust 中使用带有 serde 的嵌套枚举/结构序列化/反序列化 CSV
【发布时间】:2021-10-02 13:39:54
【问题描述】:

我想序列化/反序列化具有可变行长和内容的 CSV 文件,如下所示:

./test.csv

Message,20200202T102030,Some message content
Measurement,20200202T102031,10,30,40,2
AnotherMeasurement,20200202T102034,0,2

在我看来,最简单的表达方式是enum

#[derive(Debug, Serialize, Deserialize)]
pub enum Record {
    Message { timestamp: String, content: String }, // timestamp is String because of simplicity
    Measurement { timestamp: String, a: u32, b: u32, c: u32, d: u32 },
    AnotherMeasurement { timestamp: String, a: u32, b: u32 },
}

Cargo.toml

[dependencies]¬
csv = "^1.1.6"¬
serde = { version = "^1", features = ["derive"] }

运行以下

ma​​in.rs

fn example() -> Result<(), Box<dyn Error>> {
    let mut rdr = csv::ReaderBuilder::new()
        .has_headers(false)
        .delimiter(b',')
        .flexible(true)
        .double_quote(false)
        .from_path("./test.csv")
        .unwrap();
    for result in rdr.deserialize() {
        let record: Record = result?;
        println!("{:?}", record);
    }
    Ok(())
}

fn write_msg() -> Result<(), Box<dyn 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());
    wtr.serialize(msg)?;
    wtr.flush()?;
    Ok(())
}

fn main() {
    if let Err(err) = example() {
        println!("error running example: {}", err);
    }
    if let Err(err) = write_msg() {
        println!("error running example: {}", err);
    }
}

打印

error running example: CSV deserialize error: record 0 (line: 1, byte: 0): invalid type: unit variant, expected struct variant
error running example: CSV write error: serializing enum struct variants is not supported

是否有一个简单的解决方案可以使用 serdecsv 来做到这一点? 我觉得我错过了一两个 serde 属性,但我找不到合适的在文档中。

编辑

Netwave suggested 添加#[serde(tag = "type")] 属性。序列化现在可以工作,反序列化会出现以下错误:

error running example: CSV deserialize error: record 0 (line: 1, byte: 0): invalid type: string "Message", expected internally tagged enum Record

我所做的研究尚未找到解决方案

Is there a way to "flatten" enums for (de)serialization in Rust?

https://docs.rs/csv/1.1.6/csv/tutorial/index.html

Custom serde serialization for enum type

https://serde.rs/enum-representations.html

【问题讨论】:

    标签: csv rust enums serde


    【解决方案1】:

    制作您的enum tagged(内部专门标记):

    #[derive(Debug, Serialize, Deserialize)]
    #[serde(tag = "type")]
    pub enum Record {
        Message { timestamp: String, content: String }, // timestamp is String because of simplicity
        Measurement { timestamp: String, a: u32, b: u32, c: u32, d: u32 },
        AnotherMeasurement { timestamp: String, a: u32, b: u32 },
    }
    

    Playground

    【讨论】:

    • 感谢您的回答!这解决了序列化,给出输出Message,time,content。然而,反序列化会给出以下输出:error running example: CSV deserialize error: record 0 (line: 1, byte: 0): invalid type: string "Message", expected internally tagged enum Record我用你的答案更新了问题。
    • @LewinProbst,你使用了标题吗?很可能您需要标题,否则它将不知道将第一个字段与什么相关联。
    • 实际上它不适用于标题。我敢打赌 csv 不支持这一点,通过使用标记的我们欺骗它进行序列化,但它不够聪明,无法反序列化。
    • 嗯是的好像这个是不行的,不会加的:github.com/BurntSushi/rust-csv/issues/211
    • @LewinProbst,也许是时候编写一个完全兼容 serde 的 csv crate 了;)
    猜你喜欢
    • 1970-01-01
    • 2022-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多