Free & Private Online Browser Utility

CSV to JSON Converter (Free, No Upload)

This page reads a CSV document and writes the same information as a JSON array of objects, one object per data row, keyed by whatever the first row of the file names. It is built on the identical scanner that powers the CSV Data Explorer, so the two pages cannot disagree about where a field ends: quoted sections are tracked character by character rather than by splitting on newlines, which means a comment cell holding a line break stays one cell and a comma inside an address stays inside the address. Type inference is deliberately conservative. A value becomes a JSON number only when reading it as a number and printing it again reproduces the characters you pasted, so postal codes that begin with zero, totals written with a trailing decimal point and long numeric identifiers remain strings instead of silently changing. The words true and false become booleans in any capitalisation, and nothing else does. An empty cell is written as null when inference is on and as an empty string when it is off. Rows that do not match the header are not hidden: cells past the last column name are counted and reported above the output, and a short row is padded so every record carries the same keys. Nothing is uploaded. The text is read where you typed it, the array is assembled in this tab's memory, and the file you download is produced there too.

How to use

  1. Step 1: Paste the text or load the export

    Copy cells out of a spreadsheet, or press Load .csv and choose the file. Either way the document is read into this tab and is not sent to a server.

  2. Step 2: Correct the separator if the first-line guess is wrong

    Detection counts commas, semicolons and tabs on the header row only. Choosing semicolon, tab or pipe skips that count and rebuilds the array immediately.

  3. Step 3: Pick the JSON shape and take it away

    Use the indented array for reading and code review, or one line for pasting into a script body, then copy it or download the .json file.

Options explained

Type inference behind a round-trip test

Writes numbers and booleans only for text that survives being converted and printed back unchanged; everything else arrives quoted.

When to use: Keep it on when the JSON feeds code that does arithmetic, and turn it off when a loader applies its own schema to the values.

Separator override

Forces a comma, semicolon, tab or pipe instead of counting the first line.

When to use: Use it for pipe-separated exports, which detection never guesses, and for files whose header row is atypical of the rows beneath it.

Output layout

Switches between a two-space indented array and a single-line one, with identical content in both.

When to use: Indented for anything a person reviews, single line for a request body or a fixture inside source code.

Column and record report

Names each column with the value kinds it was written as, counts the records and columns produced, and lists dropped or padded cells.

When to use: Read it before sending an array into a database import; it is where a ragged export shows itself.

Worked example

Input: id,city,units,paid,ref 1,Bergen,3,true,A-7 2,Vigo,4,false,

Output: [ { "id": 1, "city": "Bergen", "units": 3, "paid": true, "ref": "A-7" }, { "id": 2, "city": "Vigo", "units": 4, "paid": false, "ref": null } ]

The empty cell at the end of the second row becomes null, the unquoted counts become numbers, and the reference stays text because it never was one.

Tips & limits

  • Keep the header row in what you paste: it is the only thing that names the keys, and a file without one produces objects whose property names are the first row's values.
  • Run the array through the JSON validator before it reaches code that parses it without a fallback, especially after an override changed how a column was split.
  • If a downstream service casts values itself, turn inference off rather than explaining to it why a zero-prefixed code arrived shortened.

Common problems

Each record arrived as one long string instead of columns

The separator is not one that detection counts. Pipe and colon exports need the explicit choice, after which the same rows split into keyed objects.

Numbers in a total column no longer look like the spreadsheet

Inference only quotes a value as a number when it would print back identically, so 1.0 and a leading-zero code stay strings. Switch inference off and every cell keeps the exact text that was read.

Frequently Asked Questions (FAQ)

Does a quoted field containing a line break survive the conversion?

Yes. The scanner tracks whether it is inside quotes and only treats a line break as the end of a row while it is outside them, so a multi-line comment cell stays one string value in one object. A carriage return outside quotes is dropped, and inside quotes it is kept, which means a cell exported from Windows arrives carrying the pair rather than a bare newline.

What happens if a quote in my file is never closed?

Nothing rejects it. From that mark onward the scanner is still inside a field, so the remaining commas stop separating cells and the rest of the line lands in one value. The visible symptom is fewer records than the file has rows, and the column report shows a cell holding text that belongs in several.

What happens to a row with more cells than the header names?

An object cannot hold a value with no key, so the extra cells are left out of the records rather than guessed into one. They are counted, and the page says how many were dropped above the output, which turns a silent loss into something you can see and fix in the source file.

Can this read an Excel workbook?

No. An .xlsx file is a zipped XML document, and this page reads characters rather than unzipping archives. Open the workbook in a spreadsheet application, export it as CSV, then paste or load that text here.

Why does one of my keys start with an invisible character?

That is a byte-order mark, and it is removed here before the header row is read. A parser that does not remove it hands you a key whose first character is U+FEFF: it prints as nothing, cannot be typed, and will not match the property name you wrote in your code. Both CSV pages on this site go through one scanner that strips it, so the table you profile and the array you download are named the same.

Is there a size limit on the CSV I can paste?

There is no upload to put a limit on, so the constraint is your tab's memory. The output pane stops displaying after twenty thousand characters so a huge result cannot freeze the page, while the copy button and the download still carry the complete array. A file large enough to make the tab hesitate is better converted by a script on the same machine.

Do the settings in my address bar expose the data?

No. Only the choices are written to the query string — the separator, the inference switch and the layout — so a link can reopen the page on the same settings while the rows themselves never leave the tab and never appear in the URL.

Guides that cover this tool

What happens between the last comma and the first brace

A CSV document is one line per record only when nobody has typed an address, a comment or a product description into it. The moment a field is quoted and contains a line break, the naive reading — split on newlines, then split on commas — produces a table with the wrong number of columns and no error to tell you so. This converter does not do that. It walks the text one character at a time with a single piece of state, inside quotes: yes or no, and treats a comma or a line break as structure only while that flag is off. Everything you see in the output pane comes out of that one pass.

The first surviving row names the columns. Each row underneath it becomes one object with those keys, in the same order, and the whole set is written as a JSON array so it drops straight into fetch mocks, a document database import, or a script that expects records rather than a spreadsheet. Nothing here invents nesting: a flat table in gives a flat array of objects out, because a converter that guessed at a hierarchy would be editing your data.

The separator is guessed from one line, on purpose

Automatic detection counts semicolons and tabs in the first line and compares each against the number of commas there. The separator with more occurrences wins, and because the comparison is strictly greater-than, a tie leaves the comma. That is why name;age is read as two columns while a,b;c is read as three. Two consequences are worth knowing before you rely on it: a file whose header row is unusual but whose data rows are typical will be sniffed from the header alone, and a vertical bar is not part of the automatic set at all, so pipe-separated exports need the explicit choice. The separator dropdown exists for exactly those cases — picking one skips the count entirely.

First line of the pasteAuto-detection returnsWhy
ref,customer,citycommaTwo commas beat zero semicolons and zero tabs.
ref;customer;citysemicolonTwo semicolons strictly beat two commas — there are none.
ref,customer;citycommaOne each is a tie, and a tie keeps the comma.
ref|customer|citycommaPipe is not sniffed; one column named the whole line.
ref customer citytabTwo tabs beat zero commas, so the count selects tab.

Text becomes a number only when it can go back to being the same text

Type inference is the part of a CSV-to-JSON conversion that damages data most often. The usual implementation tries Number(cell) and keeps whatever comes out, which is how a column of postal codes beginning with zero arrives as integers with the zero missing, how an invoice total written 1.0 arrives as 1, and how an identifier beyond sixteen digits can arrive quietly rounded. The rule on this page is narrower and it is checked by a test on every build: a cell is written as a JSON number only when converting it to a number and printing that number back gives the original characters.

So 42 and -3.5 become numbers. 007, 1.0, +7, 1e3 and 9007199254740993 — the first counting number a double cannot hold exactly — all stay strings, because each of them would print differently than it arrived. Booleans follow a written list rather than a guess: the words true and false in any capitalisation, and nothing else, which means a column of Yes and No is still text after conversion. An empty cell becomes null, because a missing value and an empty name are different facts and JSON has a word for the first one. Switch inference off and every cell is quoted exactly as it was read, which is the setting to use when a downstream system does its own typing.

Ragged rows are reported rather than swallowed

Real exports are untidy in two directions. A row that stops early gets the missing columns written as null (or empty text with inference off), so the records in the array all carry the same keys and a consumer can read them without a branch. A row with more cells than the header has names cannot be represented in an object at all — the extra values have no key to live under — so those cells are left out of the records, and the page counts them and says so above the output. The same applies to a header row that repeats a name: the last column holding that name is the one an object can see, and the earlier ones are reported instead of silently winning.

Two smaller behaviours are inherited from the scanner and worth stating because a parser elsewhere will differ. A carriage return outside quotes is dropped, so a file written on Windows does not leave \r at the end of every cell; inside quotes it is kept, because a quoted block is the file's own text and a multi-line cell from that same Windows export arrives holding \r\n rather than \n. A quote that never closes is not an error: everything after it accumulates into one field, so an unbalanced mark in the middle of a document swallows the rest of that line, and the count of records drops. A leading byte-order mark is removed before anything is read, which is the difference between a key called id and a key whose first character is invisible and untypeable. Rows made entirely of empty cells are dropped, so the blank line at the end of an export does not become a record of nulls.

Where a converted array gets used

The common path is an export that has to become an input. A finance team sends a CSV of subscription rows, and the script that reloads them expects records; an analytics tool downloads a cohort table that a document database has to accept as JSON lines; a front-end needs a fixture shaped like the API it is waiting for, and a table someone already has is faster to write than a mock by hand. In each case the conversion is mechanical and the interesting work is checking the first object's keys before the array goes anywhere near a production write.

That check is cheap here: the output pane is the same text the download contains, the column table beside it names each header and the kinds it was seen holding, and the counters above it state how many records and how many columns were produced. Paste the result into the JSON formatter to read it with indentation and a validator if it is going into code that has to parse it without a fallback. If the destination is a spreadsheet instead of a program, the reverse converter is the tool for that trip.

What this page is not

It reads characters, not workbooks. An .xlsx file is a zipped XML document and nothing here unzips it — open it in a spreadsheet application and export CSV first. It does not deduplicate rows, rename keys, cast a column by force, or flatten an existing hierarchy; those are transformations with opinions, and an opinion about your data belongs in code you can read. And it is a converter rather than a dataframe: the whole document lives in one string in this tab, so a paste of many megabytes competes with everything else the browser holds. The practical ceiling is the memory of the machine, and if a file is large enough that the tab hesitates, a script on the same machine is the right tool.

Nothing leaves the tab

The text is read where it was typed. There is no upload step, no queue, and no server that could log a customer list, a payroll export or an internal identifier table after the conversion finishes. That is also why the address bar can carry the settings on this page and never carries the data: ?delimiter=; is shareable, your rows are not.

Files never leave your device; calculations and transformations happen locally.

Last reviewed: 2026-11-03