Skip to content
LocalOnly
Converters

How to Convert CSV to JSON in JavaScript and Python

A single quoted comma breaks the split approach. Here is correct CSV parsing in Python and JavaScript, the byte order mark that renames your first column, and safe type conversion.

Updated October 18, 20263 min readTool runs in your browser

To convert CSV to JSON in code, you need a real CSV parser, not split(","). The CSV to JSON Converter turns a spreadsheet export into JSON in the browser, and its page explains why CSV is really a family of formats. In code, the tempting shortcut is to split each line on newlines and then on commas. It works on the first test file and fails on the first real one.

This guide shows why, how to parse CSV correctly in Python and JavaScript, and how to turn the resulting strings into the right types.

Why split fails

id,customer,note
ord_1,"O'Brien, Ana","said ""fragile""
on two lines"
ord_2,José,

This is valid CSV. The customer name contains a comma, so it is quoted. The note contains quotes, written as doubled quotes, and a line break, which is allowed inside a quoted field. Splitting the second line on commas gives four broken pieces, including "O'Brien and Ana", and splitting the file on newlines first cuts the note in half.

Convert CSV to JSON in Python

import csv
import json

with open("orders.csv", newline="", encoding="utf-8-sig") as f:
    rows = list(csv.DictReader(f))

print(json.dumps(rows, ensure_ascii=False, indent=2))

DictReader uses the first row as keys and handles quoting and line breaks inside fields. The utf-8-sig encoding removes a byte order mark if the file has one. With plain utf-8, a file saved from Excel produced the first key \ufeffid instead of id, so row["id"] raised a KeyError even though the column was clearly there.

Convert CSV to JSON in JavaScript

JavaScript has no built-in CSV parser. For large or untrusted files, use a maintained library. For known input, this function handles quoted fields, doubled quotes and line breaks inside quotes:

function parseCsv(text) {
  const rows = [];
  let row = [], field = "", inQuotes = false;
  for (let i = 0; i < text.length; i++) {
    const c = text[i];
    if (inQuotes) {
      if (c === '"' && text[i + 1] === '"') { field += '"'; i++; }
      else if (c === '"') inQuotes = false;
      else field += c;
    } else if (c === '"') inQuotes = true;
    else if (c === ",") { row.push(field); field = ""; }
    else if (c === "\n" || c === "\r") {
      if (c === "\r" && text[i + 1] === "\n") i++;
      row.push(field); rows.push(row); row = []; field = "";
    } else field += c;
  }
  if (field !== "" || row.length > 0) { row.push(field); rows.push(row); }
  return rows;
}

const [header, ...data] = parseCsv(text.replace(/^\uFEFF/, ""));
const records = data.map((cells) => Object.fromEntries(header.map((h, i) => [h, cells[i]])));

On the file above it returns three rows, with O'Brien, Ana as one field and the note intact, line break included. The replace removes a byte order mark, the same job utf-8-sig does in Python.

Converting types without losing data

Every value comes out of a CSV file as a string. Converting everything that looks numeric is the next trap:

Number("007")                    // 7      the leading zeros are gone
Number("")                       // 0      an empty cell becomes zero
Number("1e3")                    // 1000
Number("12345678901234567890")   // 12345678901234567000

Postcodes, phone numbers, IDs and anything else with leading zeros or more than 15 digits must stay strings. An empty cell usually means missing, which Number turns into a real zero. Convert per column, using a list of the columns you know are numeric, and turn empty strings into null rather than 0.

Frequently asked questions

Command Palette

Search for a tool or command