Closed jmcnamara closed 7 months ago
@lucatrv I'll check when I'm online later but do the Result<>
fields actually serialize with Serde. For example do they serialize to JSON? I don't see it as a handled type in the Serde data model and I don't remember it from the required serialization methods. Option<>
is handled as separate actions for Some
and None
but I'm not sure if Result is handled at all. It might need a serialize_with
helper function.
@jmcnamara, I confirm that the following works, but I thought you could add serialization support for Result<>
within rust_xlsxwriter
, now that I think twice about it, is it not possible because Result<>
is not in the Serde model?
use rust_xlsxwriter::{Format, FormatBorder, Workbook, XlsxError};
use serde::{Serialize, Serializer, Deserialize};
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
// Add some formats to use with the serialization data.
let header_format = Format::new()
.set_bold()
.set_border(FormatBorder::Thin)
.set_background_color("C6E0B4");
// Create a serializable struct.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
struct Student<'a> {
name: &'a str,
#[serde(serialize_with = "se_f64_or_string")]
age: Result<f64, String>,
#[serde(serialize_with = "se_f64_or_string")]
id: Result<f64, String>,
}
fn se_f64_or_string<S>(
f64_or_string: &Result<f64, String>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match f64_or_string {
Ok(f64) => serializer.serialize_f64(*f64),
Err(string) => serializer.serialize_str(string),
}
}
let students = [
Student {
name: "Aoife",
age: Ok(1.0),
id: Err(String::from("564351")),
},
Student {
name: "Caoimhe",
age: Err(String::new()),
id: Ok(443287.0),
},
];
// Set up the start location and headers of the data to be serialized.
worksheet.deserialize_headers_with_format::<Student>(1, 3, &header_format)?;
// Serialize the data.
worksheet.serialize(&students)?;
// Save the file.
workbook.save("serialize.xlsx")?;
Ok(())
}
now that I think twice about it, is it not possible because
Result<>
is not in the Serde model?
Correct. rust_xslxwriter
doesn't even see it during serialization (without a specific handler).
Folks, the derive/attribute feature is now released in v0.61.0 on crates.io. Thanks for all the input, testing and suggestions.
See the updates docs at:
https://docs.rs/rust_xlsxwriter/latest/rust_xlsxwriter/serializer/index.html#setting-serialization-headers https://docs.rs/rust_xlsxwriter/latest/rust_xlsxwriter/serializer/index.html#controlling-excel-output-via-xlsxserialize-and-struct-attributes
Correct.
rust_xslxwriter
doesn't even see it during serialization (without a specific handler).
I thought it could be seen as a Serde enum.
I thought it could be seen as a Serde enum.
@lucatrv You are right, it is handled by Serde but not rust_xlsxwriter
since it currently ignores all enum types. However, this particular type which Serde refers to as a "newtype variant" could, and probably should, be handled. If you open a new bug report for that with your first example (https://github.com/jmcnamara/rust_xlsxwriter/issues/66#issuecomment-1887684026) I push a fix for it.
rust_xlsxwriter
serialization mini-update. I've added support for adding worksheet Tables to serialized areas.
Note the #[xlsx(table_default)]
attribute in the following example. There is also #[xlsx(table_style = TableStyle::value)]
and #[xlsx(table = Table::new())]
.
This is currently on main. I'll release it by or on the weekend.
use rust_xlsxwriter::{Workbook, XlsxError, XlsxSerialize};
use serde::Serialize;
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
// Create a serializable struct.
#[derive(XlsxSerialize, Serialize)]
#[xlsx(table_default)]
struct Produce {
fruit: &'static str,
cost: f64,
}
// Create some data instances.
let items = [
Produce {
fruit: "Peach",
cost: 1.05,
},
Produce {
fruit: "Plum",
cost: 0.15,
},
Produce {
fruit: "Pear",
cost: 0.75,
},
];
// Set the serialization location and headers.
worksheet.set_serialize_headers::<Produce>(0, 0)?;
// Serialize the data.
worksheet.serialize(&items)?;
// Save the file to disk.
workbook.save("serialize.xlsx")?;
Ok(())
}
Output:
In a comment on #63 @claudiofsr said:
I've started work on implementing a custom derive macro to enable this functionality on the
derive
branch.Here is the feature set to be implemented
SerializeFieldOptions
:CustomSerializeField
Serde Container attributes:
#[serde(rename = "name")]
#[serde(rename_all = "...")]
#[serde(rename_all(serialize = "..."))]
Serde Field attributes:
#[serde(rename = "name")]
#[serde(skip)]
#[serde(skip_serializing)]
Packaging:
As a start, you can now do this:
Output:
Note the
ExcelSerialize
derived trait. Theset_serialize_headers()
doesn't use Serde serialization or deserialization (for the headers). Instead the macro generates code a custom impl for the type inline like this:It should be straight forward to support attributes like
#[rust_xlsxwriter(set_num_format="dd/mm/yyyy")]
. However, interacting (correctly) with Serde attributes will be a little trickier.I'll continue to work on this for the next few weeks and post updates when there is enough functionality to try it out with a more realistic use case.
@lucatrv for information.