transform.xlsx-to-json reads one worksheet from an uploaded .xls or .xlsx file into rows, an array of objects.
Use it when a workflow needs tabular data from a spreadsheet before a script, extract, or transform.json-to-xlsx. The step keeps a single selected sheet. It does not calculate formulas, reconstruct merged cells, or coerce values into a business schema.
Zero-config YAML stays compatible: the first effective row is the header, values are typed cached results, empty cells become "", and blank rows are skipped. Default output is rows (plus fileId when CSV is stored). Optional fields add headerless files, named or positional projection, displayed text, a rectangular A1 range, and blank-cell policy. Set includeMetadata: true to add sheet metadata and warning diagnostics. Warnings are still logged when metadata is off.
When to use it
Supported input is detected from bytes, not the filename: BIFF
.xls and OOXML .xlsx. Password-protected, corrupt, .xlsm, .xlsb, CSV, and ODS files fail clearly.
Configuration
Configuration goes inside the step’swith: block.
string
required
File input - template expression e.g. {{input.document}} resolving to a scoped $file artifact at runtime
integer | string
Sheet to read: 0-based index or exact sheet name. Omit for the first sheet.
boolean
default:"false"
If true, also write CSV to storage and include fileId. Zero-config uses SheetJS CSV of the effective sheet extent, excluding empty trailing rows and columns. When columns, range, headerRow, valueMode, blankCells, or blankRows are set, CSV matches that projection.
boolean
default:"false"
Include original cell address, type, raw/displayed value, number format and date rendering. Also includes sheet metadata with the workbook date system. Does not classify identifiers or clean names.
boolean
default:"false"
If true, include sheet metadata and diagnostics in the step output. includeCellMetadata also enables these. Omit to keep output as rows (and fileId when outputCsv is true). Warnings are still logged when this is false.
string
Output CSV filename when outputCsv is true - supports LiquidJS e.g. {{filename}}.csv
boolean | integer
Header row: a positive 1-based Excel row, or false to keep the first effective row as data. Omit to use the first effective row as the header. When range is set and this is omitted, the first range row is the header.
array<object>
Ordered output columns. Each item needs a key and exactly one source: index (0-based absolute column) or header (exact displayed header text). Named header sources require a header row. Omit to keep every column in the effective range.
"raw" | "displayed"
default:"raw"
raw (default) returns typed cached cell values. displayed returns formatted cell text (dates, leading zeros, punctuation, diacritics, embedded newlines). Formulas are never calculated.
string
Optional rectangular A1 range without a sheet qualifier, e.g. A1:D20. Disjoint ranges are rejected.
"empty-string" | "null" | "omit"
default:"empty-string"
How truly empty cells appear in each row object. Default empty-string. 0, false, and a formula that cached an empty string are not empty cells.
"skip" | "keep"
default:"skip"
skip (default) drops rows whose projected columns are all truly empty. keep retains them. Detection uses projected columns only.
object
Optional workload caps that can only lower the server defaults. Omitted fields use the server defaults.
Output
Evidence for nonblank projected data cells, linked to rows by outputRowIndex and key. Present when includeCellMetadata is true.
Array of row objects (first row = headers as keys unless headerRow is false)
File ID of stored CSV when outputCsv is true
Selected sheet metadata after projection. Present when includeMetadata or includeCellMetadata is true.
Non-fatal warnings collected while reading the sheet. Present when includeMetadata or includeCellMetadata is true. Warnings are still logged when metadata is omitted.
Limits and CSV
Server conversion caps (maxRows 100,000, maxColumns 256, maxCells 5,000,000) apply to the selected effective range that is actually materialized. With no range, empty trailing rows and columns are removed from the declared sheet extent (!ref) before limits apply. Leading blank rows/columns and real distant cells, including zero, false, and formulas, remain. An explicit range is honored in full. sheet.declaredRange records the original extent and worksheet_extent_trimmed diagnoses trimming. A huge sparse used range on a sibling sheet does not fail a small selected sheet. ZIP archives are still checked for inflate/path/entry safety before parse, and worksheet addresses must sit inside the Excel grid (1,048,576 rows × 16,384 columns). Legacy .xls files skip ZIP inspection; they still use the input-size cap, sparse reads, and the selected effective-range ceilings.
Zero-config outputCsv writes SheetJS CSV of the effective sheet extent, excluding empty trailing rows and columns. When columns, range, headerRow, valueMode, blankCells, or blankRows are set, the CSV follows that projection.
Mixed dates and identifiers
For LLM extraction from unreliable spreadsheets, use displayed values with original cell evidence:sheet (including dateSystem), diagnostics, and cellMetadata for nonblank projected data cells. Each cell has its original A1 address, output key and 0-based outputRowIndex, Excel storage type, raw cached value, displayed value, number format, and formula when present. Pass the full step output to the extraction model so it receives this evidence alongside rows.
Displayed values are recomputed from recorded number formats using the workbook’s 1900 or 1904 date system for both XLS and XLSX. Numbers with date formats receive an optional timezone-free dateValue; numeric storage type stays n. Excel’s fictitious 1900-02-29 is marked excelLeapDay: true. A date rendering describes formatting, including time-only formats, and does not prove a business meaning. Invalid/out-of-range date formatting retains the raw value instead of erasing it.
Headers are source evidence, not authoritative field classifications. Mixed date/identifier columns, strings, punctuation, and names remain intact. The parser does not guess dates from numeric magnitude or headers, convert identifiers into dates, clean names, or restore leading zeros already lost in the source file. Without a recorded date format, a serial number can remain ambiguous. The extraction model should use cell evidence and domain context, and flag uncertainty when those conflict. Formulas are never evaluated; evidence contains cached results only.
Optional cell evidence is bounded to 500,000 cells and 32 MiB of serialized characters; exceeding these bounds fails clearly without silently dropping evidence.