Excel to JSON
Read a spreadsheet as header-keyed maps, or serialise it straight to a JSON string.
Install#
dart pub add excel_plusimport 'package:excel_plus/excel_plus.dart';A sheet as a list of maps#
The first row supplies the keys, and every row after it becomes one map. This is usually what you want when the spreadsheet is feeding a model constructor or an API call.
final excel = Excel.decodeBytes(File('people.xlsx').readAsBytesSync());
for (final row in excel['People'].rowsAsMaps()) {
print('${row['name']} is ${row['age']}');
}
// {name: Alice, age: 30, active: true}
// {name: Bob, age: 25, active: false}A sheet as a JSON string#
final json = excel['People'].toJson();
// [{"name":"Alice","age":30,"active":true},{"name":"Bob","age":25,"active":false}]
final readable = excel['People'].toJson(pretty: true);The whole workbook#
Without a sheet name you get every worksheet, keyed by name and in worksheet order.
final all = excel.toJson();
// {"People":[{"name":"Alice"}],"Totals":[{"sum":42}]}
final one = excel.toJson(sheet: 'People');Choosing the header row#
Many real exports carry a title or a blank line before the real header. Point headerRow at the row you want, or pass null for an array of arrays with no header at all.
final later = sheet.toJson(headerRow: 2); // skip a two line preamble
final grid = sheet.toJson(headerRow: null); // [["name","age"],["Alice",30]]How values are exported#
Numbers and booleans keep their Dart types. Dates and times become ISO 8601 strings, because JSON has no date type.
| Cell | JSON |
|---|---|
| Text | "Alice" |
| Int, Double | 30, 1.5 |
| Bool | true |
| Date | "2024-01-31" |
| DateTime | "2024-01-31T09:30:00" |
| Time | "09:30:00" |
| Formula | its cached result, or "=SUM(A1:A9)" |
| Error | "#DIV/0!" |
| Empty | null |
Pass formulasAsText: true to export formula text instead of cached results.
Messy headers#
Every map holds a key for each column in the sheet's used width, so all rows share the same keys and a blank cell reads as null. An empty header cell falls back to its column letter, and a repeated name gets a _2 suffix, so a column is never silently dropped. Rows where every cell is empty are skipped unless you pass skipEmptyRows: false.