Paste CSV and get JSON. The header row becomes your keys, a quoted field with commas inside stays one value, and you can set the delimiter for files that use semicolons or tabs, as many European Excel exports do.
id,name,email,role,active
1,John Doe,john@example.com,Admin,true
2,Jane Smith,jane@example.com,User,false
3,Bob Johnson,bob@example.com,Editor,true [
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"role": "Admin",
"active": true
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
"role": "User",
"active": false
},
{
"id": 3,
"name": "Bob Johnson",
"email": "bob@example.com",
"role": "Editor",
"active": true
}
] A value like "Doe, John" stays one field, where splitting on commas by hand breaks on the first real export. A quoted field can also span several lines, as RFC 4180 allows, and a CRLF inside it becomes a single LF.
Semicolon, tab, pipe and a custom character are all selectable. Semicolons are common in exports from locales where the comma is the decimal separator.
Only values written as JSON numbers become numbers. 12, -3.5 and 1e5 are converted, while 00501, 1,200, .5 and 0x10 stay strings, so ZIP codes keep their leading zeros. true and false in any case become booleans, and the lowercase word null becomes null. An empty cell stays an empty string, and a cell missing from a short row becomes null.
const text = 'id,name,city\r\n1,"Lovelace, Ada",London\r\n2,"Grace ""Amazing"" Hopper",New York\r\n';
// Minimal RFC 4180 parser: handles quoted fields, "" escapes and CRLF
function parseCsv(input, delimiter = ",") {
const rows = [[]];
let field = "", inQuotes = false;
for (let i = 0; i < input.length; i++) {
const c = input[i];
if (inQuotes) {
if (c === '"' && input[i + 1] === '"') { field += '"'; i++; }
else if (c === '"') inQuotes = false;
else field += c;
} else if (c === '"') inQuotes = true;
else if (c === delimiter) { rows[rows.length - 1].push(field); field = ""; }
else if (c === "\n" || c === "\r") {
if (c === "\r" && input[i + 1] === "\n") i++;
rows[rows.length - 1].push(field); field = ""; rows.push([]);
} else field += c;
}
rows[rows.length - 1].push(field);
return rows.filter((r) => r.some((c) => c !== ""));
}
const [headers, ...body] = parseCsv(text);
const json = body.map((r) => Object.fromEntries(headers.map((h, i) => [h, r[i] ?? ""])));
console.log(JSON.stringify(json, null, 2)); About twenty lines with no dependency. Use csv-parse or papaparse when you need streaming or automatic typing.
<?php
$csv = "id,name,city\n1,\"Lovelace, Ada\",London\n2,\"Grace \"\"Amazing\"\" Hopper\",New York\n";
$handle = fopen('php://memory', 'r+');
fwrite($handle, $csv);
rewind($handle);
$headers = fgetcsv($handle, null, ',', '"', '\\');
$rows = [];
while (($line = fgetcsv($handle, null, ',', '"', '\\')) !== false) {
if ($line === [null]) continue; // skip blank lines
$rows[] = array_combine($headers, $line);
}
fclose($handle);
echo json_encode($rows, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); Use fopen('data.csv', 'r') instead of php://memory to read a file. fgetcsv() returns [null] for a blank line, hence the skip.
import csv, io, json
text = 'id,name,city\n1,"Lovelace, Ada",London\n2,"Grace ""Amazing"" Hopper",New York\n'
rows = list(csv.DictReader(io.StringIO(text)))
print(json.dumps(rows, indent=2, ensure_ascii=False))
# From a file: open("data.csv", newline="", encoding="utf-8-sig") strips a BOM csv.DictReader takes a delimiter=";" argument for semicolon files. Every value is a string; cast the columns you need.
Use a real CSV parser rather than split(). A field may contain the delimiter, a newline or a quote, as long as the whole field is wrapped in double quotes with inner quotes doubled. Splitting on the delimiter works on a sample file and breaks on the first real export. This tool handles both: rows split only on line breaks outside quotes, so a quoted multi-line field stays one value.
Encoding is the other trap. CSV cannot declare its encoding, so a file saved on Windows is often Windows-1252 while everything downstream assumes UTF-8. If accented letters look wrong after conversion, re-save the source as UTF-8 and convert again.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
Save the sheet as CSV first (File > Save As > CSV UTF-8), then upload or paste it here. To skip that step, the Excel (XLSX) to JSON tool reads the workbook directly.
Comma, semicolon, tab and pipe are presets, and Custom accepts any character. Pick semicolon for CSVs exported from Excel in most European locales.
Yes. The first row is the header, and each row after it becomes an object keyed by the header names. An empty header cell gets a generated name such as field_3.
Values written as JSON numbers, such as 12, -3.5 or 1e5, become numbers. true and false (in any case) become booleans, and the lowercase text null becomes null. Everything else stays a string, empty cells included, so a ZIP code like 00501, 1,200 and .5 keep their exact text.
A field wrapped in double quotes may contain the delimiter and doubled quotes, and it is parsed as a single value, following RFC 4180. Line breaks inside a quoted field are kept too, because rows are only split on line breaks outside quotes. A CRLF inside a field becomes a single LF.
JSON to CSV converts in the other direction. CSV to PHP and CSV to JavaScript produce code instead of data, and the table viewer shows the raw file as a table.