YAML to CSV Converter

Drop in a manifest, a values file or a bundle of documents and read it as a table. Nested blocks become dot notation columns and entries with different keys still line up. It runs in your browser, with no upload.

Want to sort, filter or pivot the result? Open the app

Reading config sideways

YAML is arranged for editing one thing at a time. A table is arranged for comparing many at once, and some questions cannot be answered by scrolling a manifest:

  • Which services have no memory limit? As a table it is one glance down a column. As YAML it is twenty blocks and a hope you did not skim past one.
  • What changed between two releases? Convert both and diff the tables, without the indentation noise that makes YAML diffs annoying.
  • Answering someone who does not read YAML. Finance wants instance sizes, compliance wants retention periods, neither wants a repository link.
  • A bulk edit round trip. Convert to CSV, change forty values in a spreadsheet, then regenerate with the CSV to YAML tool.

Worked example: nested blocks become dotted columns

Two services with the resource blocks every Kubernetes user has stared at:

- name: billing-api
  replicas: 3
  resources:
    requests:
      cpu: 250m
      memory: 512Mi
    limits:
      cpu: "1"
      memory: 1Gi
  labels:
    tier: backend
- name: web-frontend
  replicas: 2
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits:
      cpu: 500m
      memory: 512Mi
  labels:
    tier: frontend

The CSV, exactly as it comes out:

name,replicas,resources.requests.cpu,resources.requests.memory,resources.limits.cpu,resources.limits.memory,labels.tier
billing-api,3,250m,512Mi,1,1Gi,backend
web-frontend,2,100m,256Mi,500m,512Mi,frontend

Seven columns from two levels of nesting, and the comparison is now trivial: billing-api requests 250m and caps at 1, web-frontend requests 100m and caps at 500m. The path is in the header, so nothing is ambiguous.

One detail is worth naming. The source wrote cpu: "1" with quotes, because Kubernetes wants that field as a string, and the CSV cell just says 1. CSV has no types, so a quoted string and a bare number look identical once they are cells. To send the data onward, convert to JSON, where the distinction survives.

What counts as a row

YAML has no single shape, so a converter has to read the file and pick the meaning that matches. There are four, applied without asking:

  • A sequence of mappings gives one row each. The common case, and the one the worked example uses.
  • A single mapping gives one row. A whole config file becomes one wide row: server.host, server.port, server.tls.enabled. Handy for comparing environments.
  • A sequence of plain values gives one column named value. A list of regions becomes a one-column table rather than an error.
  • Several mapping documents give one row per document. The manifest bundle shape, with a note saying how many there were. A mix of lists and mappings has its rows combined instead.

Missing keys are not an error in any of these. Columns are the union of every entry's keys in first-seen order, so an entry with an extra field widens the table instead of being trimmed.

Where flattening stops being honest

Nested mappings flatten cleanly, because a path through mappings ends at a single value and a dotted header names it exactly. Lists do not, and this is the part most converters fudge or crash on. A list stays in its cell as JSON text. A service with ports: [8080, 8443] and tags: [prod, eu] produces this:

name,ports,tags
api,"[8080,8443]","[""prod"",""eu""]"

The alternative would be columns named ports.0 and ports.1, which works right up until one service has six ports and the table grows six columns that are empty for everyone else. A field holding a list of mappings also raises a note, because splitting there would invent rows the file never had. A cell of JSON text is still readable in a spreadsheet and parseable in code: a compromise, but a visible one.

Gotchas worth knowing

  • Anchors and merge keys are resolved first. A service inheriting settings through <<: *defaults shows the inherited values in its row, not a blank, which is what you want when auditing what is deployed.
  • Unquoted leading zeros are already numbers on arrival. zip: 01730 is the integer 1730 to any YAML parser, so the cell reads 1730. Quoting it in the source keeps it intact. Unquoted no stays text.
  • Duplicate keys are refused with a line number, rather than the conversion silently picking one. Tabs in indentation report the same way.
  • A file of empty entries is refused too, because a list of empty mappings has no fields to make columns from.
  • Files up to 100 MB, no row cap, no daily quota. Commas and quotes inside values are escaped properly, and the preview shows the first rows so you can check the columns before downloading.

Frequently Asked Questions

How does it decide what counts as a row?

By the shape of the file: a sequence of mappings gives one row per entry, a single mapping gives one row, a sequence of plain values gives one column called value, and several mapping documents give one row each.

How are nested maps turned into columns?

With dot notation, to whatever depth the file goes. A block holding resources, then requests, then cpu becomes a column headed resources.requests.cpu. It is the same spelling our JSON to CSV tool produces.

What happens to lists inside the YAML?

A list stays in one cell as JSON text, so ports of 8080 and 8443 become the cell [8080,8443]. A list of mappings also raises a note, because splitting it would invent rows the file never had.

My entries do not all have the same keys. Do the odd ones get dropped?

No. The columns are the union of every entry's keys, in first-seen order, and an entry that lacks one gets a blank cell rather than a lost field.

Why did 01730 become 1730, and why did a quoted value lose its quotes?

Unquoted, 01730 is an ordinary integer to a YAML parser, so it loads as 1730 before the CSV is written; quote it in the source and it stays 01730. The quotes go because CSV has no types at all.

Is anything uploaded, and how large a file can it take?

Nothing is uploaded. The parser is JavaScript running in your tab, with nothing kept between visits. Files up to 100 MB work here, with no row cap and no daily quota. Past that the widget offers the full editor.

Convert your YAML to CSV

No sign-up, no upload, no row cap. Nested blocks flattened into dotted columns.

Back to the converter