excel_to_json: turn .xlsx and CSV spreadsheets into JSON

pub version pub points pub likes downloads tests license

Turn any Excel or CSV spreadsheet into JSON with one call.
Formula results, dates, merged cells, title rows: handled. Pure Dart, every platform, plus a CLI.

🇧🇷 Converta planilhas Excel (.xlsx) e CSV em JSON ou em mapas Dart, em Dart puro: resultado de fórmulas, datas, células mescladas, cabeçalho em qualquer linha.


final json = const ExcelToJson().toJson(bytes);
{"People": [{"Name": "Ana", "Age": 30, "Admin": true, "Total": 120.5},
            {"Name": "Bruno", "Age": 41, "Admin": false, "Total": 98}]}

✨ Features

Feature Details
📑 .xlsx and CSV Detected from the content; Excel, Google Sheets and LibreOffice files
🧮 Formula results =SUM(...) gives the number Excel saved, not the formula text
📅 Real dates Date-formatted cells come as dates, times as times, in any format
🏷️ Header anywhere Skip title rows with headerRow, or use column letters with no header
🔗 Merged cells Optionally repeat a merged value in every cell it covers
🔑 Clean keys Trimmed, unique (Name, Name_2), or snake_case / camelCase
🗂️ Pick sheets Convert every sheet or only the ones you name
🖥️ Command line excel-to-json report.xlsx > report.json
🌍 Pure Dart No plugins: Android, iOS, web, desktop, CLI and server

🚀 Install

dart pub add excel_to_json      # or: flutter pub add excel_to_json

📖 Usage

The converter takes the bytes of an .xlsx or .csv file, wherever they come from, and detects which one it is.

From a file (CLI, server, desktop)

import 'dart:io';

import 'package:excel_to_json/excel_to_json.dart';

final bytes = File('people.xlsx').readAsBytesSync();
print(const ExcelToJson().toJson(bytes));

Picked by the user in Flutter

With file_picker 13:

final file = await FilePicker.pickFile(
  type: FileType.custom,
  allowedExtensions: ['xlsx', 'csv'],
);
if (file == null) return; // cancelled

final json = const ExcelToJson().toJson(await file.readAsBytes());

From an asset or a download

// Flutter asset
final data = await rootBundle.load('assets/people.xlsx');
final map = const ExcelToJson().toMap(data.buffer.asUint8List());

// HTTP
final response = await http.get(Uri.parse('https://example.com/people.xlsx'));
final rows = const ExcelToJson().toMap(response.bodyBytes)['People'];

Without freezing a Flutter screen

A large spreadsheet takes a moment to convert. Run it off the UI thread with compute from package:flutter/foundation.dart:

final map = await compute(const ExcelToJson().toMap, bytes);

Map or JSON

toMap gives Map<String, List<Map<String, Object?>>>, sheet name to rows, ready to feed your models. toJson gives the same shape as a string.

final sheets = const ExcelToJson().toMap(bytes);
for (final row in sheets['People']!) {
  print('${row['Name']} is ${row['Age']}');
}

🧾 Real-world spreadsheets

A title above the header

Reports often start with a title and a blank line. Say which row has the column names, numbered as Excel shows it:

const converter = ExcelToJson(config: ExcelToJsonConfig(headerRow: 3));

No header at all

With headerRow: null every row is data and the keys are the column letters:

const converter = ExcelToJson(config: ExcelToJsonConfig(headerRow: null));
// [{"A": "Ana", "B": 30}, {"A": "Bruno", "B": 41}]

A column with data but no name in the header also gets its letter, so nothing is lost.

Merged cells

In the file, a merged cell holds its value only in its top-left cell. To repeat it in every row and column it covers, as it looks on screen:

const converter = ExcelToJson(config: ExcelToJsonConfig(fillMergedCells: true));
// Region "South" merged over two rows:
// [{"Region": "South", "City": "Curitiba"}, {"Region": "South", "City": "Porto Alegre"}]

Keys ready for code

const converter = ExcelToJson(
  config: ExcelToJsonConfig(keyStyle: KeyStyle.snakeCase),
);
// "Data de Nascimento" -> "data_de_nascimento", "Endereço (Rua)" -> "endereco_rua"

KeyStyle.camelCase gives dataDeNascimento. The default keeps the header text, with extra spaces removed. Repeated names become Name, Name_2, Name_3.

Only some sheets

const converter = ExcelToJson(config: ExcelToJsonConfig(sheets: ['Sales']));

Naming a sheet the file does not have throws an ExcelToJsonException that lists the sheets it does have. To keep every sheet but the hidden ones (often lookup tables), set includeHiddenSheets: false.

🖥️ Command line

dart pub global activate excel_to_json

excel-to-json people.xlsx                         # JSON on stdout
excel-to-json report.xlsx --header-row 3 --keys snake --pretty > report.json
excel-to-json data.csv --sheet Sheet1 --empty-rows

excel-to-json --help lists every option; they mirror ExcelToJsonConfig and also take the form --option=value. Exit codes: 64 bad usage, 65 unreadable spreadsheet, 66 file not found, 74 output not written.

🔤 Cell values

In the spreadsheet In toMap In toJson
Text String string
Whole number int number
Decimal, %, currency double (5% is 0.05); int when whole number
TRUE / FALSE bool boolean
Date, date and time ISO 8601 String (or DateTime, see below) ISO 8601 string
Time of day HH:mm:ss String (or Duration) HH:mm:ss string
Elapsed time [h]:mm HH:mm:ss, hours can pass 24 (or Duration) string
Formula The result Excel saved as its type
Error (#N/A) The error text string
Empty cell null null
Any CSV field String; empty field null, "" is '' string or null

CSV values stay text so codes like 01310100 keep their leading zero; convert them in your model.

⚙️ All options

const converter = ExcelToJson(
  config: ExcelToJsonConfig(
    headerRow: 1,               // row with the column names; null for none
    sheets: null,               // only these sheets; null for all
    includeHiddenSheets: true,  // false: leave out sheets hidden in Excel
    keyStyle: KeyStyle.original, // or snakeCase, camelCase
    includeEmptyRows: false,    // keep rows with no values
    useDateTimeStrings: true,   // false: DateTime/Duration in toMap
    formulaResults: true,       // false: the formula text instead
    fillMergedCells: false,     // repeat merged values
  ),
);

toJson always writes dates as strings, since JSON has no date type.

Files written by tools that do not compute formulas (some libraries) have no saved result; those cells give the formula text. With formulaResults: false, a formula filled down a column gives its text only in the first cell: the others store just a reference to it, so they give their result.

📄 CSV files

CSV is detected automatically and comes as one sheet named Sheet1. The delimiter (,, ; or tab) is the one used consistently on the first lines, so files saved by Excel in Brazil, which use ; and decimal commas, work as they are, title line included. Quoted fields, line breaks inside quotes, a UTF-8 BOM and Windows-1252 files (Excel's default on Windows) are handled.

🚨 Errors

Files that cannot be read throw ExcelToJsonException, with the original error in cause: corrupt files, legacy .xls files (save them as .xlsx), and sheets named in sheets that do not exist.

try {
  const ExcelToJson().toJson(bytes);
} on ExcelToJsonException catch (e) {
  print('Could not convert: ${e.message}');
}

⬆️ Migrating from 1.x

2.0 removes the built-in file picker, so the package is pure Dart and works everywhere, with no native plugin or minimum iOS version imposed on your app.

1.x 2.0
await ExcelToJson().convert() (opened a picker, returned String?) Pick the file yourself (example), then ExcelToJson().toJson(bytes)
ExcelToJsonConfig(verbose: ...) Removed
ExcelToJsonConfig(dateFormat: ...) Removed (it was never applied)
Formulas came as their text They come as their result; formulaResults: false restores the text
Columns with a blank header were dropped They keep their data under the column letter

2.0 also fixes 1.x bugs: text cells crashed the conversion, a blank header cell shifted the following columns, useDateTimeStrings: false crashed, and empty rows were never skipped.

❓ FAQ

Does it read .xls or .ods?

No. .xlsx (Excel 2007 and later, also saved by Google Sheets and LibreOffice) and CSV. Save older .xls and .ods files as .xlsx.

Does it read images, comments or colors?

No, only cell values: text, numbers, booleans, dates and formula results. Images, comments, formatting and charts are left out.

How big a file can it handle?

Sheets are read as a stream of XML events, not loaded as a document tree: a 20,000-row, 10-column sheet converts in about 2 seconds on a laptop, using tens of megabytes. The whole result is kept in memory, as a map.

Why is a formula cell showing the formula text?

The file has no saved result for it, which happens when it was written by a tool that does not compute formulas. Open and save it in Excel, Google Sheets or LibreOffice and the results are stored.

🤝 Contributing

Bug reports and pull requests are welcome; see CONTRIBUTING.md. A small .xlsx that shows the problem helps the most. If the package helps you, ⭐ the repository and 👍 it on pub.dev.

📄 License

MIT.

Libraries

excel_to_json
Converts spreadsheets (.xlsx and .csv) to JSON or Dart maps, in pure Dart.