Command Palette

Search for a command to run...

CSV to JSON: The Type Inference Will Eat Your Zip Codes

CSV to JSON: The Type Inference Will Eat Your Zip Codes

T
Toolz Team
|Jul 7, 2026|16 min read

A client sent me a 12,000-row customer export to load into a staging database. I converted it, imported it, and shipped. Two days later their ops lead asked why a few hundred customers in New Jersey had four-digit zip codes.

07102 had become 7102. Every zip starting with a zero, across the whole file, quietly amputated. Not by a bug — by a feature. Type inference looked at 07102, saw something that matched "a number," and helpfully converted it to one. Leading zeros are not a thing numbers have.

I built the CSV to JSON Converter on toolz.dev, and it does this too. I'm going to show you exactly where, because a converter that silently changes your data is more dangerous than one that just fails.

TL;DR: The CSV to JSON Converter turns a CSV into an array of objects, using the first row as keys, running entirely in your browser. It always infers types and you can't switch that off in the UI — which means 071027102, 1250.501250.5, empty cells → null, and true/false → booleans. Big integers past 2⁵³ stay strings, which saves your Twitter IDs. It's comma-only in the interface, so semicolon CSVs from European Excel won't parse. Duplicate headers are a hard error rather than a silent overwrite — that one's a genuine improvement over most parsers. Check the output before you import, and use the JSON Formatter to eyeball it.


What Does This Converter Actually Do to Your Data?

Every value in a CSV is text. 29 is the characters 2 and 9. JSON has real types, so a converter has to decide: is 29 the number 29 or the string "29"? There's no right answer in general, and every CSV parser guesses.

This one guesses. Always. Here's the complete rule set, straight from the code and verified by running it:

CSV value JSON output Type
29 29 number
07102 7102 number — zero gone
1250.50 1250.5 number — cent gone
true / TRUE true boolean
null / NIL null null
(empty cell) null null
9007199254740993 "9007199254740993" string
1e3 1000 number
+5 "+5" string
.5 ".5" string
NO "NO" string
+8801712345678 "+8801712345678" string
2024-01-05 "2024-01-05" string

A few of these deserve a closer look, because the pattern isn't obvious.

The number regex is strict, and that's load-bearing. It matches ^-?\d+$ and nothing else. That's why +5 and .5 survive as strings — no leading plus, no bare decimal point. It's also why +8801712345678 — a Bangladeshi phone number, and I test with mine — comes through intact. Strip that + and you'd lose it to numeric conversion. So the strictness of the regex is accidentally protecting phone numbers, and only phone numbers that kept their country-code plus.

Big integers are handled correctly, which surprised me. Anything outside JavaScript's safe integer range (±2⁵³−1) is deliberately kept as a string rather than converted and mangled. Snowflake IDs, Twitter IDs, and 19-digit database keys survive. Most naive converters call parseInt and hand you 9007199254740992 where you had ...93. This one checks the bound first.

NO stays a string — which quietly dodges the Norway problem that eats YAML files. Different format, different parser, but worth noting: the country code NO is a coin-flip for how a config parser treats it, and here it's safe.

Empty means null, not empty string. And here's a small documentation bug I found writing this: the tool's own "How it works" panel says "Empty values are preserved as empty strings in the JSON output." They aren't. inferType('') returns null. The UI text is wrong; the code is what ships. I'm fixing the copy.


Why Does 07102 Become 7102, and What Do I Do About It?

Because a zip code is not a number and a computer can't tell.

07102 matches "digits, optionally negative" perfectly. parseInt("07102", 10) is 7102. The leading zero carried meaning — it was the data — and numbers have no way to hold it. The same thing happens to:

  • Zip codes: 07102, 01950, anything in New England or New Jersey
  • Product SKUs: 00451
  • Phone numbers without a plus: 0171...
  • Bank/routing codes, employee IDs, any padded identifier
  • Money: 1250.501250.5, and 2100.002100

That last one is worth sitting with. 1250.50 and 1250.5 are the same number and a different string. If you're rendering currency downstream, $1250.5 is going to show up in a UI somewhere, and someone will file a bug that takes an hour to trace back to a CSV conversion three weeks earlier. (The real lesson there is to store money as integer cents or a decimal string, never a float — but that's a fight for a different article.)

Right now, in this tool, there is no toggle to turn inference off. The underlying function takes an inferTypes option and the UI never passes it, so it's on, permanently, at the default. That's a gap, and it's the top item on my list for this tool.

Until it ships, your options:

  1. Look at the output. Seriously — this is why the tool shows JSON in a pane instead of just downloading it. Scan your ID columns. It takes ten seconds and it's how I should have caught the zip codes.
  2. Quote-proof it upstream. Quoting in the CSV won't help — the parser strips quotes and then infers, so "07102" still becomes 7102. Instead, make the value non-numeric at the source: export as ZIP-07102, or add a prefix column in the spreadsheet.
  3. Post-process. Convert, then fix the columns you know are identifiers: data.forEach(r => r.zip = String(r.zip).padStart(5, '0')). Ugly, but honest and auditable.
  4. Use a real parser for real pipelines. For anything scheduled or automated, csv-parse or Papa Parse in Node with cast: false will do this properly. A browser tool is for the one-off.

What Does the Parser Get Right?

I've been hard on it, so here's the other half — and some of this is genuinely better than what you'll find elsewhere.

Duplicate headers are a hard error. Feed it a,b,a and it refuses: Duplicate headers found: a. Most parsers silently let the last column win, so a column of data vanishes without a word. Failing loudly here is the right call — you cannot have two keys with the same name in a JSON object, and pretending otherwise loses data.

Quoted fields are handled properly, per RFC 4180. Commas inside quotes stay put; "" becomes a literal ". The sample data ships with "Mike, Jr." specifically to prove it.

Newlines inside quoted fields work. This is where hand-rolled CSV splitting usually dies — someone splits on \n and a multi-line address field detonates the whole file. The row splitter tracks quote state character by character, so "line1\nline2" stays one field.

Blank lines are skipped, so trailing newlines and the stray empty row Excel leaves at the end don't produce phantom objects.

Ragged rows don't crash. Too few fields and the missing keys come back null; too many and the extras past your header count are dropped silently. (That last part I'd call debatable rather than correct — a warning would be better than a quiet discard.)

One RFC deviation to know about: values get trimmed, including inside quotes. RFC 4180 §2.4 says spaces are part of the field and shouldn't be ignored, so " John " should be " John ". This tool gives you "John". That's a deliberate convenience — 99% of the time trailing whitespace in a CSV is an accident you want gone — but if you're round-tripping data where padding is significant, it's a lossy step and the spec is not on my side.


Why Won't My Semicolon CSV Parse?

Because the interface is comma-only, and this catches every European export.

Excel in a locale that uses a comma as the decimal separator writes CSVs with semicolons as the field delimiter. Perfectly valid, extremely common, and this tool will not parse it. Paste a;b and you get a single key literally named a;b with the value "1;2". It doesn't error — it just produces one useless column, which is worse than erroring.

The parsing function accepts a delimiter option. The UI never exposes it. Same class of gap as the inference toggle.

Workarounds:

  • Find-and-replace ;, in a text editor first — safe only if no field contains a comma inside quotes.
  • Re-export from Excel with "CSV UTF-8 (comma delimited)".
  • Use the CSV Viewer instead — it does delimiter detection by scoring column-count consistency across candidate delimiters, so it will read your semicolon file. It won't convert to JSON, but it'll show you what you have.

Tabs have the same problem. So does pipe-delimited.


How Do I Convert CSV to JSON on Toolz.dev?

Step 1: Open the converter

Go to the CSV to JSON Converter. Sample data is loaded and already converted, so you can see the shape immediately.

Step 2: Paste your CSV

Paste into the left pane. There's no file upload — it's a textarea, so open your .csv in a text editor and copy it across. For a few thousand rows that's fine. For a 200 MB export, use a real parser.

Your first row must be headers, and you need at least one data row. A single line returns CSV must have at least a header row and one data row.

Step 3: Convert

Hit Convert to JSON. You get an array of objects, one per row, keys from your header, indented two spaces. The row count appears above the output — check it against what you expected, since it's the cheapest possible sanity test.

Step 4: Read the output before you trust it

The step everyone skips and I skipped once. Look at your ID columns. Look at your money columns. If a zip code shows up unquoted in the JSON, it's a number now.

Step 5: Copy or download

Copy for the clipboard, Download JSON for a data.json file.


When Should I Not Use a Browser Converter?

Being honest about the boundary:

Situation Use
One-off, a few thousand rows CSV to JSON
Sensitive data you can't upload This — it never leaves your browser
Anything scheduled or automated csv-parse / Papa Parse in Node
Semicolon or tab delimited CSV Viewer, or fix the delimiter
Zip codes, SKUs, padded IDs Anything with cast: false
Nested output from dotted headers A script — this produces flat objects only
100k+ rows Streaming parser; the browser holds it all in memory
Going the other way JSON to CSV

That "nested output" row is worth stating plainly since older versions of this guide claimed otherwise: headers like user.name and address.city do not become nested objects. You get a flat object with a key literally named "user.name". There's no dot-notation expansion, no header transformation, no camelCase conversion. Flat objects, keys exactly as typed in your header row — spaces, hashes, and all. A header of Order # gives you a key of "Order #", which is legal JSON and awkward JavaScript (row["Order #"]).

The reason to reach for this tool is the same reason as the rest of the site: your data doesn't go anywhere. If you're converting a customer export with real names and emails in it, that's not a small thing — pasting it into a server-side converter means handing a third party a file of personal data, which is a conversation with your DPO you'd rather not have. Here the parsing runs in your tab. Turn off your Wi-Fi and it still works.


Frequently Asked Questions

Why did my zip code lose its leading zero?

Because type inference converted it to a number, and numbers can't store leading zeros. 07102 matches the "digits only" pattern, so it becomes 7102. This affects zip codes, padded SKUs, employee IDs, and any identifier that happens to be all digits. Quoting it in the CSV won't help — quotes are stripped before inference runs. Check your ID columns in the output, or prefix the values at the source to make them non-numeric.

Can I turn off automatic type conversion?

Not in the interface. The underlying function supports an inferTypes option, but the UI doesn't expose it, so inference is always on. If you need raw strings, use a library parser like Papa Parse or csv-parse with casting disabled, or fix the affected columns after conversion.

Does it support semicolon or tab delimited files?

No — the interface is comma-only. A semicolon file parses into one nonsense column rather than erroring, so watch for that. Replace the semicolons with commas first (safe only if no quoted field contains a comma), re-export from Excel as comma-delimited, or use the CSV Viewer, which detects delimiters automatically.

What happens to empty cells?

They become null, not empty strings. Note that the tool's own "How it works" panel currently says the opposite — the panel text is wrong and the code is correct. Cells containing the literal text null or nil also become null.

Can I convert a CSV without a header row?

No. The first row is always treated as the header, and a file with only one row returns an error saying it needs a header plus at least one data row. If your data has no header, add one in a text editor before pasting.

Does it handle commas and line breaks inside quoted fields?

Yes, both. The parser tracks quote state character by character, so a field like "Mike, Jr." keeps its comma, and a quoted field containing a newline stays a single value instead of splitting the row. Escaped double quotes ("") become a single literal quote, per RFC 4180.

What happens with duplicate column names?

It refuses to convert and tells you which header is duplicated. That's deliberate — a JSON object can't have two identical keys, so the alternative is silently dropping a column. Many parsers do exactly that; rename the columns and convert again.

Is there a row limit?

No enforced limit, but everything runs in your browser's memory: the CSV text, the parsed array, and the formatted JSON string all exist at once. A few thousand rows is comfortable; six-figure row counts will make the tab struggle. For files that large, use a streaming parser in Node instead.

Is my data uploaded anywhere?

No. Parsing happens in your browser and your CSV is never transmitted, which is what makes it safe for customer exports and other data you couldn't legitimately paste into a server-side tool. Verify it the easy way — open DevTools, watch the Network tab while converting, or just turn off your Wi-Fi and see that it still works.


The Short Version

CSV to JSON looks like the most boring conversion in computing and it isn't, because the moment a parser decides what your strings mean, it can be wrong in ways that don't announce themselves. No error, no warning, just four-digit zip codes surfacing two days later.

So: convert, then read the output. Check the columns that are identifiers pretending to be numbers. If they came out unquoted, you've lost something. That habit costs ten seconds and would have saved me an embarrassing email.

The CSV to JSON Converter is good at the one-off job — quoted fields, embedded newlines, big-integer IDs, and loud failures on duplicate headers, all without your data leaving the browser. It's comma-only, it always infers types, and it produces flat objects. Now you know all three before they bite.

Going the other direction: JSON to CSV. For inspecting a messy CSV first: CSV Viewer. For checking the output: JSON Formatter. And if your data's heading for a config file, JSON to YAML. The wider tour is in the JSON tools guide and the coding tools guide.

Comments

0 comments

0/2000 characters

No comments yet. Be the first to share your thoughts!