CSV, JSON, YAML, XML: choosing a data format
These five formats overlap enough that people treat them as interchangeable and differ enough that converting between them silently destroys information. The differences are not stylistic. They are differences in what each format can express at all, and every conversion is a projection onto a smaller space.
The type system is the real difference
Almost every conversion bug traces back to one question: does the format record what type a value is?
CSV and INI do not. Every value is text, and any typing is inferred by whatever reads the file. This is why ZIP codes lose their leading zeros, why part numbers turn into dates, and why a configuration value of false is frequently truthy in the consuming program.
JSON does, but with a limited vocabulary: string, number, boolean, null, array, object. That covers most needs and leaves real gaps. There is no date type, so dates travel as strings by convention and every system agrees on a different convention. There is no integer type distinct from float, and numbers beyond 2^53 lose precision silently in any JavaScript-based consumer, which is why large database identifiers are routinely transmitted as strings.
YAML has JSON types plus implicit resolution rules that try to guess. This is convenient until it is not, which is the origin of the well-known problem where an unquoted NO becomes the boolean false rather than the country code for Norway.
XML has no types at all without an accompanying schema, and with one it has an extremely elaborate type system that almost nobody uses fully.
Structural capability
The second axis is shape. CSV is strictly two-dimensional: rows and columns, nothing nested. JSON and YAML nest arbitrarily. XML nests and additionally distinguishes attributes from child elements, and preserves the order of mixed text and markup.
This is why converting nested JSON to CSV is the conversion that most often goes wrong. There is no correct answer, only a choice between flattening keys into dotted paths, which produces sparse wide tables, and serialising branches into a single cell as embedded JSON, which produces a table you cannot query. Both are defensible and neither round-trips.
Going the other way is safe: CSV maps cleanly onto an array of flat objects. Conversions that expand capability are safe; conversions that reduce it are where information goes missing.
What each format is genuinely best at
Each of these formats won its niche for a reason.
- CSV is unbeatable for tabular data at volume. It streams, so a hundred-million-row file can be processed without loading it into memory, and every analytical tool on earth reads it. Use it for exports, bulk loads, and anything spreadsheet-shaped.
- JSON is the right default for APIs and machine-to-machine exchange. Parsers are fast and universally available, the structure is unambiguous, and it is compact enough. Its weakness is human editing: no comments, and a single trailing comma is a syntax error.
- YAML is for configuration a human maintains. Comments, readable nesting without punctuation noise, and multi-line strings make it far more pleasant to edit than JSON. Its weakness is that significant whitespace and implicit typing make it easy to get subtly wrong.
- XML is for documents and for exchange where a formal contract matters. Schema validation, namespaces, and mixed content are real capabilities that JSON lacks. It remains dominant in publishing, finance, and healthcare for those reasons rather than through inertia alone.
- INI and .env are for flat key-value configuration where simplicity is the point. Beyond one level of nesting they stop being suitable.
Conversions that lose information
It is worth having a concrete list, because the losses are predictable.
Going in these directions, expect to lose the item named:
- YAML to JSON: comments, anchors and aliases as shared structure, multi-document streams, block scalar styles.
- JSON to CSV: all nesting, the distinction between null and empty string, and type information.
- XML to JSON: attribute and element distinction unless encoded by convention, mixed content ordering, namespaces as scopes, comments, and the difference between a one-item list and a single value.
- Anything to INI: nesting beyond one level, and all type information.
- CSV to anything: nothing, because CSV holds almost nothing. This is why it is the safe direction.
Practical defences
Quote aggressively in YAML. Any scalar whose meaning depends on being a string should be quoted, particularly version numbers, country codes, and anything with leading zeros.
Transmit large integers as strings in JSON. Any identifier that might exceed 2^53 will eventually be corrupted by a JavaScript consumer, and the corruption is silent.
Use ISO 8601 for dates everywhere, written as YYYY-MM-DD or with a full timestamp and explicit offset. It sorts lexicographically, no locale misreads it, and it eliminates the ambiguity between American and European day-month ordering.
Normalise XML-derived arrays in code rather than trusting the shape, because the one-item case will differ from the many-item case and it will do so in production.
Validate at boundaries. A schema, whether JSON Schema, XSD, or a runtime validator, catches these problems where they enter your system rather than three services downstream.
Frequently asked questions
Which format should an API return?
JSON, unless you have a specific reason otherwise. Parser support is universal, it is compact, and its type model matches what most languages expect. XML remains appropriate where formal schema validation is contractually required.
Why do my large ID numbers come back wrong?
JavaScript numbers are IEEE 754 doubles, so integers beyond 2^53 cannot be represented exactly. Any JSON parsed in a browser or Node service will silently round them. Transmit such identifiers as strings.
Is YAML safe to use for configuration?
Yes, with two habits: quote any scalar whose meaning depends on being text, and validate against a schema. Its implicit typing rules are the main hazard and quoting neutralises them.