The same two records shown as CSV on the left and as JSON on the right, field by field.
The same two records as CSV and as JSON. The values are the ones that usually break: NO is a boolean in YAML unless it is quoted, and 1.10 and 2.0 change value the moment a converter types them as numbers instead of keeping them as strings.

Why convert CSV to JSON at all

CSV is the format data arrives in; JSON is the format code wants. Spreadsheets, CRM exports, bank statements, analytics downloads: they all leave their systems as CSV because every tool since the 1980s can write it. But the moment that data meets an API, a config file, a NoSQL database or a JavaScript frontend, it needs to be JSON. The conversion is the bridge between "I exported this from Excel" and "my code can iterate over it".

The mechanical part is simple, one row becomes one object. The annoying parts are everything around it: delimiters that differ by locale, quotes around cells that contain commas, numbers that are not really numbers, and empty cells that could mean zero, empty or unknown. Those details are where hand-rolled line.split(',') scripts fail, usually silently, and they are what this page is careful about.

How to use this converter

Paste CSV into the left pane, or drop a .csv file on it, and the JSON appears on the right while you type. The delimiter is detected from the first line, so comma, semicolon, tab and pipe files all work without settings.

  1. Paste or drop your CSV. An Excel export, a database dump, three lines you typed by hand; anything tabular works.
  2. Check the numbers. The strip under the panes shows input and output size, line count and the number of rows converted. If the row count is off by one, your header assumption is wrong; toggle --no-header.
  3. Copy or download. The result goes to your clipboard or saves as a .json file.

--no-header

Treats the first row as data instead of column names. The output switches from an array of objects to an array of arrays, which is the right shape for matrix-like data (measurements, grids, coordinates) where naming columns adds nothing.

--keep-strings

Turns off all type casting. Every cell stays exactly the text it was, including empty cells as "" and the words true and null as words. Use it when the consuming system does its own parsing or when text fields legitimately contain values that look like something else.

--nested

Reads a dot in a header cell as a level of nesting, so the columns contact.email and contact.city produce "contact": { "email": …, "city": … } instead of two flat keys with dots in their names. On by default, and it does nothing to headers that contain no dots.

This is the exact inverse of the --flatten option in our JSON to CSV converter, which is where dotted headers come from in the first place. With both on, a nested API response survives the round trip JSON → CSV → JSON unchanged, which is the point: a spreadsheet is a fine place to edit records, and getting the original structure back afterwards should not be a scripting job. Switch it off if your column names legitimately contain dots, v1.2.total for instance, which would otherwise be split into three levels.

--pretty

Indents the JSON with two spaces, on by default. Turn it off for a single-line result when the JSON goes straight into a request body or an environment variable.

What shape the JSON takes

With a header row, each data row becomes one object and the header cells become its keys:

CSVJSON
id,name,active
1,Ada,true
2,Grace,false
[{"id": 1, "name": "Ada", "active": true},
 {"id": 2, "name": "Grace", "active": false}]
Same file with --no-header[["id", "name", "active"],
 [1, "Ada", true], [2, "Grace", false]]

Every row gets every header key, even where the row is shorter than the header; missing cells are filled with null. A header cell that is empty produces a placeholder key (column_3 for the third column), because JSON objects cannot have nameless members. Rows that are entirely empty are skipped rather than emitted as useless empty objects, and a trailing newline at the end of the file does not create a phantom row.

The output is always a JSON array at the top level. That is the shape import endpoints, JSON.parse consumers and data frames expect, and it means a one-row CSV still produces an array with one element rather than a bare object that behaves differently downstream.

A table comparing what CSV and JSON can represent: comments, typed values, explicit null, nested structures and a top-level list.
Both formats can represent the same things here, so this direction loses nothing structural. That is not true of the way back for every pair, which is why the table is per direction and not per format.

Type casting without surprises

CSV has exactly one data type, text. JSON has six. Deciding which cells become numbers, booleans and null is where most converters quietly damage data, so this one follows a strict rule: a cell is only cast when the cast is lossless. Concretely, a cell becomes a number only if converting it to a number and back yields the identical text.

Cell textConverted valueWhy
4242 (number)Round trip is exact
91.591.5 (number)Round trip is exact
007"007" (string)Leading zeros would vanish
1.10"1.10" (string)Would collapse to 1.1; version numbers survive
9007199254740993stringAbove 2^53, JavaScript numbers lose precision
true / falsebooleanLiteral match only
empty cell / nullnullHonest "no value"

The practical payoff: phone numbers, ZIP codes with leading zeros, article numbers like 0043 and long database IDs come through as the strings they really are. Our experience with blind-casting converters is that exactly these columns get mangled, and nobody notices until a customer with ZIP code 01067 complains. If you want no casting at all, --keep-strings switches the whole mechanism off.

Delimiters, quotes and line breaks

The converter reads the first line and picks whichever of comma, semicolon, tab or pipe occurs most often as the delimiter. That covers the real-world spread: US-locale Excel and most programming output use commas, European Excel uses semicolons, database dumps and clipboard copies from spreadsheets use tabs, and Unix tooling occasionally uses pipes.

split on every comma
id,name,city
7,"Hopper, Grace",NYC

→ 4 fields, one row broken
RFC 4180 quoting
id,name,city
7,"Hopper, Grace",NYC

→ 3 fields, name = Hopper, Grace

Inside the rows, the parser follows RFC 4180, the closest thing CSV has to a standard:

  • Quoted cells. "Hopper, Grace" is one cell, comma and all. The quotes are syntax, not content, and are removed.
  • Escaped quotes. Inside a quoted cell, "" means one literal quote character: "5\" display" reads as 5" display.
  • Line breaks in cells. A quoted cell can span lines. Address fields and comment columns from spreadsheet exports rely on this, and a parser that splits on newlines first (the classic quick script) shreds them.
  • CRLF and LF. Windows and Unix line endings are both accepted, mixed files included.

What the parser deliberately does not do is guess beyond that. A file that mixes delimiters per row, or uses a quoting style no spec describes, produces exactly what it says; the row count in the stats strip is your first check that the structure came through as expected.

CSV from Excel and Google Sheets

The most common source of CSV is a spreadsheet, and each exporter has quirks worth knowing. Excel offers two relevant formats in "Save as": CSV UTF-8 (comma separated), which is the one you want, and the legacy CSV (comma separated), which on Windows still writes the system's ANSI code page and turns umlauts into garbage on the way through. If you see ä where ä should be, the file was saved with the wrong one of the two; re-export, the converter itself works on whatever text it receives.

On German, Austrian and most European systems, Excel writes semicolons instead of commas, because the comma is the decimal separator there. This page detects that automatically. What it cannot repair is a decimal-comma number in an unquoted comma-delimited file, since 3,14 in that context is genuinely two cells; that damage happens at export time, not here.

Google Sheets (File → Download → CSV) is more predictable: always UTF-8, always commas, quotes where needed. It exports only the current sheet, as does Excel, so multi-sheet workbooks need one export per sheet.

Pitfalls to check after converting

  • The header assumption. If the first data row went missing, the converter used it as headers. Toggle --no-header and it comes back. The row count in the stats strip makes this obvious.
  • Duplicate header names. Two columns both named email collapse into one key per JSON object, and the second value wins. Rename columns before exporting if both matter.
  • Whitespace in headers. Header cells are trimmed, but a key like "First Name" keeps its inner space. That is valid JSON, just awkward to access as row["First Name"]; rename at the source if it bothers you.
  • Numbers that were codes. The lossless-cast rule protects leading zeros, but a plain 4711 that is semantically an ID still becomes a number. If the consuming system compares IDs as strings, use --keep-strings.
  • Encoding damage from the source. Mangled umlauts like ä mean the CSV was exported with the wrong encoding; that is only fixable at the source. The UTF-8 byte order mark that Excel puts in front of the first header is handled here, the converter strips it before parsing.

CSV to JSON, the questions that follow

Is it safe to convert customer data or internal exports with an online CSV converter?

Only with a converter that parses in your browser. Spreadsheet exports are where names, email addresses, order values and salary columns live, and uploading one to an unknown server is a data transfer your privacy policy almost certainly does not cover, GDPR included. Parsing runs here as JavaScript inside your tab, with no upload and no logging, and the page works offline. For any other tool, watch the Network tab in devtools while converting, and if a request goes out, treat that export as disclosed.

How do I convert a CSV file to JSON?

Pick the route that matches where the file already is. For a one-off file or a snippet in your clipboard, a browser converter is fastest: drop the .csv in, take the JSON out. For a file you convert repeatedly, script it, with pandas or the csv module in Python, csvtojson or Papa Parse in Node.js, or the miller command mlr --icsv --ojson cat data.csv on the shell. For data that already lives in a database, exporting JSON directly usually beats the CSV detour. The decisions are the same in all of them: which row holds the headers, which delimiter the file uses, and whether numbers should stay text.

How do I convert CSV to JSON in Python?

Two lines with the standard library: import csv, json; json.dump(list(csv.DictReader(open("data.csv", newline="", encoding="utf-8-sig"))), out). DictReader takes the first row as keys and gives you one dict per row, which is the array-of-objects shape APIs expect. Use encoding="utf-8-sig", because Excel writes a byte order mark that otherwise ends up glued to your first column name. With pandas it is pd.read_csv("data.csv").to_json("data.json", orient="records"), where orient decides the shape and dtype=str keeps IDs and leading zeros intact. Both read the whole file into memory; for very large exports iterate over DictReader and write one JSON line per row.

How are commas inside values handled?

Through quoting, as RFC 4180 defines it. A cell like "Hopper, Grace" keeps its comma because it is wrapped in double quotes, a doubled quote ("") inside a quoted cell becomes a literal quote, and quoted cells may even contain line breaks. This parser implements all three rules, so an Excel export with messy text cells comes through intact.

Why do some numbers stay strings in the JSON output?

Because casting them would change the data. This converter casts a cell to a number only when the round trip is lossless: "42" becomes 42, but "007" stays a string (the leading zeros would vanish), "1.10" stays a string (it would collapse to 1.1) and a 17-digit ID stays a string because JavaScript numbers lose precision above 2^53. Most converters cast blindly; this check is the difference between a file you can trust and one you have to audit.

Can I convert a CSV that uses semicolons instead of commas?

Yes, automatically. Excel in German, French and most other European locales exports with semicolons, so the converter looks at the first line and picks the delimiter that actually occurs there: comma, semicolon, tab or pipe. You do not have to declare anything; paste the file and the right delimiter is used.

How do I convert CSV to JSON in JavaScript or Node.js?

Use a parser, not split(","), because a naive split breaks on the first quoted comma or line break inside a cell. In the browser and in Node, Papa Parse handles quoting, delimiters and streaming: Papa.parse(text, { header: true, skipEmptyLines: true }).data gives you the array of objects. csv-parse and csvtojson are the common Node alternatives, both with stream APIs so a large file never has to fit in memory. For a file the user picks in a browser, read it with file.text() and hand that to the parser; for an upload endpoint, pipe the request stream straight into the parser instead of buffering it.

Can I convert an Excel file (XLSX) to JSON here?

Not directly; this tool reads CSV text, and .xlsx is a zipped binary format. The two-step route works fine: open the file in Excel or Google Sheets, save or download it as CSV, then drop that CSV here. Only the active sheet survives that export, so repeat it per sheet if you need more than one.

What is the difference between CSV and JSON?

CSV is a flat table: rows and columns, every value plain text, no nesting and no types. JSON is a tree: objects, arrays, strings, numbers, booleans and null, nested as deep as needed. That is why CSV to JSON is easy (each row becomes a flat object) while the reverse direction needs decisions about flattening. CSV wins for spreadsheets and bulk data, JSON wins for APIs and anything structured.

How do I get nested JSON out of a flat CSV?

Through a convention, because a CSV carries no nesting information of its own: a dot in a header name is read as a level of nesting, so the columns contact.email and contact.city produce "contact": { "email": …, "city": … }. That is what the --nested option here does, and it is the exact inverse of the dot-path flattening in the JSON to CSV direction, which is where such headers usually come from in the first place. The convention is not universal, so switch the option off when your column names legitimately contain dots, a header like v1.2.total for instance, which would otherwise be split into three levels. For anything more structured than that, arrays of objects inside a cell for example, convert to flat objects and reshape in your own code, where the intent is explicit.

Which JSON structure do APIs usually expect, an array of objects?

Almost always yes: [{"id": 1, "name": "Ada"}, ...] is the shape REST endpoints, import scripts and libraries like pandas (via read_json) or JavaScript’s Array.map expect. That is exactly what this converter produces in its default mode. The array-of-arrays form from --no-header is for matrix-like data where column names carry no meaning.

Why does my CSV import produce empty rows or a column with no name?

Because the file has a trailing delimiter or trailing blank lines that the producing program considered harmless. A header line ending in a comma declares one more column than it names, so every parser invents a placeholder key for it, and a file ending with several newlines yields rows where every field is empty. Excel is the usual source: deleting the contents of a row leaves the row itself in the sheet, and the export dutifully writes it out. Open the file in a text editor rather than a spreadsheet to see it, strip the trailing delimiter and the blank lines, and re-run the conversion. Most parsers have a skip-empty-lines option, which hides the symptom without fixing the export.