tafia / calamine

A pure Rust Excel/OpenDocument SpreadSheets file reader: rust on metal sheets
MIT License
1.67k stars 156 forks source link

RangeDeserializer not works with serde(rename)+serde(deserialize_with) #264

Open sinyo-matu opened 2 years ago

sinyo-matu commented 2 years ago

Am I doing some wrong?

this not works

use calamine::DataType;
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct InputRowRaw {
    #[serde(rename(deserialize = "other_name"))]
    #[serde(deserialize_with = "de_opt_native_datetime")]
    purchase_time: Option<NaiveDateTime>,
}

fn de_opt_native_datetime<'de, D>(deserializer: D) -> Result<Option<NaiveDateTime>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let data_type_res = calamine::DataType::deserialize(deserializer);
    match data_type_res {
        Ok(data_type) => match data_type {
            DataType::DateTime(f) => Ok(DataType::DateTime(f).as_datetime()),
            _ => Ok(None),
        },
        Err(e) => Err(e),
    }
}
pub const HEADERS: &[&str] = &["other_name"];

#[cfg(test)]
mod tests {
    use calamine::{RangeDeserializerBuilder, Reader, Xlsx};

    use crate::{InputRowRaw, HEADERS};

    #[test]
    fn not_works() {
        let mut book: Xlsx<_> = calamine::open_workbook("workbook.xlsx").unwrap();
        let sheet = book.worksheet_range_at(0).unwrap().unwrap();
        let de = RangeDeserializerBuilder::with_headers(HEADERS)
            .from_range(&sheet)
            .unwrap();
        for row in de {
            let InputRowRaw { purchase_time } = row.unwrap();
            println!("{purchase_time:?}")
        }
    }
}

you can check it at calamine-test

emirror-de commented 1 year ago

Hi there,

I had the same problem. It turned out that I was missing #[serde(default)] as well as DataType::Float(f). Applying it to your code makes your test pass.

#[derive(Serialize, Deserialize, Debug)]
struct InputRowRaw {
    #[serde(rename(deserialize = "other_name"))]
    #[serde(deserialize_with = "de_opt_native_datetime")]
    #[serde(default)]
    purchase_time: Option<NaiveDateTime>,
}
DataType::Float(f) | DataType::DateTime(f) => Ok(DataType::DateTime(f).as_datetime()),

References: #[serde(default)] is required because when using deserialize_with