【问题标题】:Rust Calamine Optional Parser Stops Reading Rows at First Empty CellRust Calamine 可选解析器在第一个空单元格处停止读取行
【发布时间】:2021-08-17 21:32:59
【问题描述】:

我一直在按照炉甘石文档中的示例从电子表格中提取值。

它们工作正常,但是当尝试使用解析空单元格时,可选的解析器示例不起作用。我想知道你们中是否有人能发现我在这里缺少的东西。

我们有一个包含 5 列的电子表格。

这是有效的代码:

fn example<P: AsRef<Path>>(xls_path: P) -> Result<(), Error> {
    let mut workbook: Xlsx<_> = open_workbook(xls_path)?;
    let range = workbook
        .worksheet_range("Billing")
        .ok_or(Error::Msg("Cannot find 'Billing' sheet"))??;

    let mut iter = RangeDeserializerBuilder::new().from_range(&range)?.enumerate();
    
    while let Some((i, result)) = iter.next() {
        println!("{:?}", result);
        let (
            invoice_num,
            billing_date,
            client,
            amount,
            date_received,
            quarter,
            direct_expenses,
            expenses,
            net,
            total_expenses,
            gross_profit,
        ): (
            Option<String>,
            Option<String>,
            Option<String>,
            Option<f64>,
            Option<String>,
            Option<String>,
            Option<f64>,
            Option<String>,
            Option<String>,
            Option<f64>,
            Option<f64>,
        ) = result?;

        println!("Number {:?}", i);
        println!("Invoice #: {:?}", invoice_num);
        // print them all..
    }

    Ok(())
}

手动将它们分配给可选值会导致打印每一行,无论其中哪些单元格是空的。

现在示例代码采用这种方法:

#[derive(Serialize, Deserialize, Debug, Clone)]
struct RawExcelRow {
    #[serde(rename = "INVOICE #", deserialize_with = "de_opt_string")]
    invoice_num: Option<String>,
    #[serde(rename = "BILLING DATE", deserialize_with = "de_opt_string")]
    billing_date: Option<String>,
    #[serde(rename = "CLIENT/PROJECT", deserialize_with = "de_opt_string")]
    client_project: Option<String>,
    #[serde(rename = "AMOUNT", deserialize_with = "de_opt_f64")]
    amount: Option<f64>,
    #[serde(rename = "DATE REC'D", deserialize_with = "de_opt_string")]
    date_received: Option<String>,

}

fn example2() -> Result<(), Box<dyn std::error::Error>> {
    if env::args().count() > 2 {
        println!("{}", env::args().count());
        return Err(Box::new(Error::from("Enter the filename or leave blank for default")));
    }

    let path_string = format!("{}", env::current_dir().unwrap().display());
    let file_name = &env::args().nth(1).ok_or(Error::Msg("Unkown filename"))?;
    let xls_path = Path::new(&path_string).join(file_name);
    
    let mut excel: Xlsx<_> = open_workbook(xls_path)?;

    let range = excel
      .worksheet_range("Billing")
      .ok_or(calamine::Error::Msg("Cannot find Billing sheet"))??;

    let mut iter = RangeDeserializerBuilder::new().from_range::<_, RawExcelRowDate>(&range)?.enumerate();
    while let Some((i, Ok(row))) = iter.next() {
        println!("Number {}", i);
        println!("Invoice #: {:?}", row.invoice_num);
        // print them all..
    }
    Ok(())
}

还有反序列化器:

fn de_opt_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error> 
where
    D: serde::Deserializer<'de>,
{
    let data_type = calamine::DataType::deserialize(deserializer);
    match data_type {
        Ok(calamine::DataType::Error(e)) => Ok(None),
        Ok(calamine::DataType::Float(f)) => Ok(Some(f.to_string())),
        Ok(calamine::DataType::Int(i)) => Ok(Some(i.to_string())),
        Ok(calamine::DataType::String(s)) => Ok(Some(s)),
        Ok(calamine::DataType::DateTime(d)) => Ok(Some(d.to_string())),
        _ => Ok(None),  
    }
}

fn de_opt_f64<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let data_type = calamine::DataType::deserialize(deserializer);
    match data_type {
        Ok(calamine::DataType::Error(_)) => Ok(None),
        Ok(calamine::DataType::Float(f)) => Ok(Some(f)),
        Ok(calamine::DataType::Int(i)) => Ok(Some(i as f64)),
        _ => Ok(None),
    }
}

每当我向结构RawExcelRow 添加一列时会发生什么,它会停在第一行,其中与结构相对应的一列有一个空单元格。我期望看到的值是None,但我看到了错误:Deserializer error: missing field 'DATE REC'D',因为 DATE REC'D 是其中一行中第一个为空的单元格。例如,第 18 行在 DATE REC'D 列中具有第一个空单元格。所以只会解析第 1-17 行。

我查看了源代码,但似乎找不到它调用此错误,而不是仅填写 None 值。我也尝试过使用基于访问者的反序列化器,但无济于事。

附言

调用RangeDeserializerBuilder::with_headers(&amp;COLUMNS) 其中&amp;COLUMNS 是一个列名数组,就像在示例中所做的那样,而不是::new() 没有区别,因为::new() 只使用RangeDeserializer 的默认值,它使用所有标题。

【问题讨论】:

    标签: rust serde


    【解决方案1】:

    这个问题的答案有点难以捉摸,因为炉甘石没有很好地记录日期。

    链接中的答案提到提供属性#[serde(default)] 以在缺少输入时使用Option::default 值。

    即使在单元格为空时,这也会导致炉甘石继续运行。尽管以“可选”解析器为例,Calamine 文档仍然忽略了这一点。

    How can I deserialize an optional field with custom functions using Serde?

    知道了这一点,这个问题在某种程度上与答案中列出的那个问题和其他问题重复。但是,我并没有删除这个问题,因为问题实际上是什么并不是很明显。

    解决方案

    将结构更改为如下所示(并添加日期反序列化器)解决了问题:

    #[derive(Serialize, Deserialize, Debug, Clone)]
    struct RawExcelRow {
        #[serde(default, rename = "INVOICE #", deserialize_with = "de_opt_string")]
        invoice_num: Option<String>,
        #[serde(default, rename = "BILLING DATE", deserialize_with = "de_opt_date")]
        billing_date: Option<NaiveDate>,
        #[serde(default, rename = "CLIENT/PROJECT", deserialize_with = "de_opt_string")]
        client_project: Option<String>,
        #[serde(default, rename = "AMOUNT", deserialize_with = "de_opt_f64")]
        amount: Option<f64>,
        #[serde(default, rename = "DATE REC'D", deserialize_with = "de_opt_date")]
        date_received: Option<NaiveDate>,
    
    }
    

    【讨论】:

      猜你喜欢
      • 2017-01-19
      • 1970-01-01
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-17
      • 1970-01-01
      相关资源
      最近更新 更多