DEV Community

Dev Nestio
Dev Nestio

Posted on

I Built a Bidirectional CSV ↔ JSON Converter With Zero Dependencies (RFC 4180 Compliant)

The use case comes up constantly: you have a spreadsheet export you need as JSON for a mock, or an API response you need in CSV to share with a non-engineer. Most tools handle the happy path fine — it's the edge cases that bite you.

I built a CSV ↔ JSON Converter that handles quoted fields, embedded commas, escaped quotes, and mixed delimiters correctly. All in the browser, no server.

👉 https://devnestio.pages.dev/csv-json-converter/

What's Wrong With split(',')?

If you've ever tried to parse CSV with a naive split, you know the answer: it breaks the moment a field contains a comma.

name,note
"Smith, John","He said ""hi"""
Enter fullscreen mode Exit fullscreen mode

Split on commas and you get 4 fields instead of 2. The devnestio converter uses a state machine parser that handles this correctly per RFC 4180.

What It Does

CSV → JSON (Object Array)

Row 1 is treated as headers; each subsequent row becomes an object:

id,name,active
1,Alice,true
2,Bob,false
Enter fullscreen mode Exit fullscreen mode
[
  { "id": 1, "name": "Alice", "active": true },
  { "id": 2, "name": "Bob", "active": false }
]
Enter fullscreen mode Exit fullscreen mode

Type inference runs automatically: numeric strings become numbers, true/false become booleans.

JSON → CSV

Object arrays convert back to CSV. Keys from all objects are unioned into the header row, so rows with missing keys don't misalign columns:

[
  { "id": 1, "name": "Alice" },
  { "id": 2, "name": "Bob", "city": "Tokyo" }
]
Enter fullscreen mode Exit fullscreen mode
id,name,city
1,Alice,
2,Bob,Tokyo
Enter fullscreen mode Exit fullscreen mode

Multiple Delimiter Support

Toggle between comma, tab (TSV), and semicolon. Useful for:

  • Spreadsheet copy-paste (produces tab-separated output)
  • European CSV files (semicolon-separated because commas are decimal separators)

Smart Type Inference

CSV is all strings. Converting to JSON raises the question: do you turn "123" into 123 or leave it as "123"?

The rules this tool uses:

  • Matches ^-?\d+(\.\d+)?$number
  • true / false (case-insensitive) → boolean
  • Empty string → empty string "" (not null — preserves information)
  • Everything else → string
  • Leading zeros are preserved as strings ("007" stays "007")

That last rule matters. Zip codes and IDs that start with zero get silently corrupted if you aggressively cast to numbers.

The Parser: State Machine, Not Split

The core CSV parser tracks a single inQuotes flag and processes one character at a time:

function parseCSV(text, delimiter) {
  const rows = [];
  let field = '', row = [], 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 === delimiter) { row.push(field); field = ''; }
      else if (c === '\n') { row.push(field); rows.push(row); field = ''; row = []; }
      else if (c !== '\r') field += c;
    }
  }
  if (field !== '' || row.length) { row.push(field); rows.push(row); }
  return rows;
}
Enter fullscreen mode Exit fullscreen mode

One flag handles commas, newlines, and escaped quotes correctly. Skimping on this is how you end up with a converter that "mostly works" until it hits real data.

Validated with 120/120 passing tests covering embedded commas, embedded newlines, escaped quotes, empty fields, mismatched keys, and all three delimiter modes.

No External Libraries

papaparse would make this a 10-line project. But devnestio's rule is single HTML file, zero external dependencies — so data you paste never touches a CDN script, and the tool works offline after first load.

Try It

For anyone who regularly shuffles data between spreadsheets and APIs:

👉 CSV ↔ JSON Converter — devnestio

All tools: https://devnestio.pages.dev

If you hit a CSV that breaks it, share it in the comments — real-world edge cases are the best test cases.

Top comments (0)