Skip to content
sheetsmith

Values and types

public record Money(BigDecimal amount, Currency currency) { }
@Component // application-wide: every Money column, in every sheet class
public class MoneyConverter implements CellConverter<Money> {
@Override
public CellValue convert(Money value, ConversionContext context) {
return CellValue.number(value.amount().doubleValue());
}
}
@ExcelSheet
public record InvoiceRow(
@ExcelColumn(header = "Invoice", order = 10) String number,
@ExcelColumn(header = "Total", order = 20, format = "#,##0.00") Money total,
@ExcelColumn(header = "Currency", order = 30, converter = CurrencyCodeConverter.class) Currency currency) {
}
public class CurrencyCodeConverter implements CellConverter<Currency> {
@Override
public CellValue convert(Currency value, ConversionContext context) {
return CellValue.text(value.getCurrencyCode());
}
}
@Bean
CellConverter<Instant> instantConverter(@Value("${app.export.zone:Europe/Rome}") ZoneId zone) {
return (value, context) -> CellValue.dateTime(LocalDateTime.ofInstant(value, zone));
}
@ExcelSheet
public record AuditRow(
@ExcelColumn(header = "At", order = 10, format = "dd/mm/yyyy hh:mm:ss") Instant at,
@ExcelColumn(header = "User", order = 20) String user) {
}

The zone is an explicit application choice. Without the converter, AuditRow violates V-10.

Postal codes, product codes, IBANs and long numeric identifiers must not become numbers: leading zeros would disappear and digits beyond the 15th would be lost.

@ExcelSheet
public record AccountRow(
@ExcelColumn(header = "Postal code", order = 10) String postalCode, // String: already text
@ExcelColumn(header = "Reference", order = 20, converter = LongAsText.class) long reference) {
}

Keep such values as String in the sheet class whenever possible.