Developer · 3 min read

Converting Between JSON, YAML and CSV Without Losing Data

YAML has types JSON lacks, JSON has nesting CSV lacks, and CSV has ambiguities that will eat your leading zeros. A map of what survives each trip.

These three formats are not interchangeable representations of the same thing. Each can express structures the others cannot, and conversion between them is lossy in specific, predictable ways.

What each format can hold

Capabilities by format
JSONYAMLCSV
NestingYesYesNo
Typesstring, number, bool, nullThose plus dates, and moreNone: everything is text
CommentsNoYesNo
References / anchorsNoYesNo
Multiple documents in one fileNoYesNo
Duplicate keysUndefined behaviourError in strict parsersDuplicate headers allowed

JSON to YAML: safe, with one caveat

JSON is a subset of YAML 1.2, so any valid JSON document is already valid YAML and converts cleanly. The output is usually more readable, which is why configuration formats keep drifting toward YAML.

The caveat is on the way back. YAML features that JSON has no equivalent for, comments, anchors and aliases, multiple documents, non-string keys, are silently dropped or flattened when you convert to JSON. If your YAML file is documentation as much as configuration, converting it to JSON and back deletes the documentation.

YAML to JSON: watch the implicit typing

YAML guesses types from unquoted scalars, and its guesses are occasionally spectacular:

  • no, off, n become false under YAML 1.1 rules; yes, on, y become true
  • 1.0 becomes a float, so a version string turns into a number and loses its precision
  • 012 may be read as octal in older parsers, giving 10
  • 2026-09-09 becomes a date object in YAML 1.1, which JSON then has to render back to a string in whatever format the library chooses
  • null, ~ and an empty value are all null

The fix is always the same: quote anything that must stay a string. Version numbers, country codes, phone numbers, postcodes and IDs are the usual casualties.

JSON to CSV: flattening is a decision

CSV is a grid. JSON is a tree. Converting one to the other means deciding how to project the tree onto the grid, and there is no universally correct projection.

For an array of flat objects it is straightforward: keys become the header row, objects become rows. For anything nested you have to choose:

{"id":1,"user":{"name":"Ada","tags":["x","y"]}} → id, user.name, user.tags
1, Ada, "x|y"

Dotted paths for nested objects are the near-universal convention. Arrays have no convention, so pick one and be consistent: joining with a separator is compact but breaks if the data contains the separator; indexing creates a variable number of columns; exploding into multiple rows duplicates the parent fields.

Objects with different key sets are the other decision. Either union all keys and leave blanks, which is usually right, or restrict to keys present in every object, which silently discards data.

CSV to JSON: everything is a string until you decide otherwise

CSV carries no type information at all. Every value arrives as text, and the converter has to guess whether "123" is a number, whether "true" is a boolean, and whether an empty cell is an empty string, a null, or a missing key. Those are three different things and the choice matters downstream.

Then there are CSV's own ambiguities, which have no single right answer because there is no single CSV standard:

  • Delimiter: comma, semicolon in much of Europe, or tab
  • Quoting: a field containing the delimiter, a quote, or a newline must be quoted, and an embedded quote is doubled
  • Line endings: RFC 4180 specifies CRLF; plenty of producers emit LF
  • Encoding: UTF-8 is the sane default, but exports from spreadsheet software often arrive as UTF-16 or a legacy code page, sometimes with a byte order mark that becomes part of the first header name
  • Headers: optional, duplicated, or containing whitespace that only shows up as a mysteriously missing column

The round-trip rule

Before converting anything you care about, run the round trip on a sample and compare it with the original. JSON to CSV to JSON should give you back what you started with. When it does not, the diff shows you exactly which fields your flattening rule cannot represent, and it is far cheaper to learn that on a sample than on a production export.

Our JSON to YAML converter handles both directions, the JSON Flattener collapses nesting to dotted keys before a CSV export, and the JSON to CSV converter unions keys across objects rather than dropping the ones that are not universal. For the rules that keep JSON itself sane, see JSON formatting: the rules people get wrong.

Common questions

Why did my YAML "no" become false?

YAML 1.1 treats yes, no, on and off as booleans. This famously bites country codes: Norway’s NO becomes false. Quote the value ("no") to force it to stay a string. YAML 1.2 narrowed this to true/false only, but many parsers still run 1.1 semantics.

How should nested objects become CSV columns?

The common convention is a dotted path: {"user":{"name":"Ada"}} becomes a column named user.name. Arrays are harder, since there is no single right answer: index them (tags.0, tags.1), join them into one cell with a separator, or explode into multiple rows. Pick one and document it, because the reverse conversion has to know which you chose.

Why do my IDs lose their leading zeros?

Because a spreadsheet opened the CSV and decided 00123 was the number 123. CSV carries no type information, so every consumer guesses. If the data must survive a spreadsheet round trip, use a format that carries types, or accept that identifiers need a non-numeric prefix.