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"""
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
[
{ "id": 1, "name": "Alice", "active": true },
{ "id": 2, "name": "Bob", "active": false }
]
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" }
]
id,name,city
1,Alice,
2,Bob,Tokyo
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
""(notnull— 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;
}
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)