我正在解析 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 是一种非常有限的格式,因此正确处理它非常困难。您可以通过简单的方法来完成此操作,而无需更改结构(因此不必要地更改程序的其余部分),只需跳过错误行即可:
for record in rdr.deserialize::<Record>() {
let Ok(record) = record else {
continue;
};
}