The JSON is flattened exactly as the JSON to CSV converter does it. Each nested object becomes columns named by path — address.city, address.geo.lat — and the header is the union of every path in every row, so a field only some objects have still gets a column, next to its siblings. An array is written as JSON text in one cell, or with Expand arrays as one column per element (tags.0, tags.1). An empty object or array keeps its own cell as {} or []. JSON Lines, one object per line, is read too, and an object whose values are all arrays of objects — the shape Excel to JSON produces with All sheets — becomes one sheet per key. An array of arrays is taken as rows already, without a header.
The difference from a CSV is that every cell has a type. A JSON number is written as a number and true or false as an Excel boolean, while a string is always text, even when it looks like a number: "007" stays 007 with its zeros, and "=1+2" is stored as the characters =1+2, not a formula, because the cell says it is text. That also means formula injection cannot happen the way it does when a CSV is opened. Dates are the one place a string can change type, and only when you ask: with ISO dates on, 2026-09-29 and 2026-09-29T13:45:00 are written as Excel date serials with a yyyy-mm-dd or yyyy-mm-dd hh:mm:ss format; a timestamp with Z or an offset is converted to UTC, since an Excel date has no time zone, and a date before 1900, which Excel cannot store, stays text.
Precision is lost before conversion begins in one case. JSON.parse turns an integer past 2^53 into the nearest number JavaScript can hold, so 12345678901234567890 becomes 12345678901234567000; the source text is scanned for such integers and any that were rounded are reported. Excel adds a second limit, 15 significant digits, so a 16-digit number is flagged as well. Ids belong in quotes, as strings. The workbook itself is written without a library: the sheet XML, a shared string table, a stylesheet with a bold header font and the date formats, column widths estimated from the content, and a ZIP compressed with this page’s own DEFLATE encoder. Sheet names are cut to Excel’s 31 characters without \ / ? * : [ ], and control characters XML cannot carry are removed, with a warning.