使用 serde 有条件地反序列化 csv 记录

问题描述 投票:0回答:1

我正在解析 csv 表,进行一些处理并导出 JSON 文件。 CSV 记录是删节的 Grafana 面板,因此我有一些结构来抽象

gridPos
targets
alert
和其他字段。 Grafana 也有行,它们比面板简单得多,只有网格位置、标题和类型属性。我想检查我当前正在处理的行的第一列是否是“__row”,然后如果是这种情况则跳过反序列化大多数字段。不幸的是,Serde 没有
skip_deserializing_if
宏,所以我正在寻找解决方法。这是我尝试过的:

let mut rdr = csv::Reader::from_reader(file);
for record in rdr.deserialize() {
  if record.unwrap().name == "__row" {
    // do some things
    continue;
  }

  // Record is an struct that abstracts my template csv record
  let record: Record = record?;
  // continue as usual if it's a regular panel, and not a row

这不起作用,我收到以下错误消息:

   |
68 |     for record in rdr.deserialize() {
   |                       ^^^^^^^^^^^ cannot infer type of the type parameter `D` declared on the method `deserialize`
69 |
70 |         if record.unwrap().name == "__row" {
   |            --------------- type must be known at this point
   |
help: consider specifying the generic argument
   |
68 |     for record in rdr.deserialize::<D>() {
   |                                  +++++

我尝试将函数签名更改为

pub fn process_alerts<D: for<'de> serde::Deserialize<'de>>
,但这让我注意到它无法解决我的问题,因为调用
record.as_ref().unwrap()
仍然会出现恐慌,并且我需要某种方法来访问
name
字段,即使其他字段
Record
字段为空。

对此有什么好的解决方案吗?我不想让每个记录字段都为

Option<T>
,也不想使用
#[serde(default)]
,因为我希望应用程序在解析常规面板时指定未知变体时出错。

新编辑:此摘录是问题的摘要。我还提供了一个可重现的示例,可以在 Rust Playground

上运行
use serde::{Serialize, Deserialize};
use csv::Reader;

#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all(deserialize = "snake_case"))]
enum Foo {
    Bar,
    Ham,
    Eggs
}

#[derive(Deserialize, Clone)]
struct Record{
    name: String,
    foo: Foo,
    baz: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = "\
    name,foo,baz
    Panel 1,bar,my text
    __row,,
";
    let mut rdr = Reader::from_reader(data.as_bytes());

    for record in  rdr.deserialize() {
        let _record: &Record = record.as_ref().unwrap();

        if &_record.name == "__row" {
          // do some things
          continue;
        }

        let record: Record = record?;
        // do something else...
  }

  Ok(())


  // this will panic when reading the second csv record
  /*
  thread 'main' panicked at src/main.rs:28:48:
  called `Result::unwrap()` on an `Err` value: Error(Deserialize { pos: Some(Position { byte: 37, line: 3, record: 2 }), err: 
  DeserializeError { field: None, kind: Message("unknown variant ``, expected one of `bar`, `ham`, `eggs`") } })
  */

}

本质上,我需要一种方法来

skip_deserializing_if
,这将允许我以不同的方式处理以
__row
作为第一列值的 csv 记录。

csv rust deserialization serde
1个回答
0
投票

CSV 是一种非常有限的格式,因此正确处理它非常困难。您可以通过简单的方法来完成此操作,而无需更改结构(因此不必要地更改程序的其余部分),只需跳过错误行即可:

for record in rdr.deserialize::<Record>() {
    let Ok(record) = record else {
        continue;
    };
}
© www.soinside.com 2019 - 2024. All rights reserved.