Skip to content

JSON to CSV Converter

Flatten a JSON array of objects into a CSV file. First object's keys become the header row.

    Runs entirely in your browser. Nothing is uploaded.

    This tool converts JSON to CSV in your browser; nothing leaves your machine. For the reverse direction, see CSV to JSON.

    The straightforward case

    Flat array of objects → CSV with one row per object:

    JSON:
    [
      {"name": "Alice", "age": 30, "city": "NYC"},
      {"name": "Bob",   "age": 25, "city": "Boston"}
    ]
    
    CSV:
    name,age,city
    Alice,30,NYC
    Bob,25,Boston
    

    The header is the union of keys; each row is one object. Cells are JSON-escaped for CSV (commas, quotes, newlines are wrapped in double quotes per RFC 4180).

    Heterogeneous keys

    When objects have different keys, the union is the header:

    JSON:
    [
      {"name": "Alice", "age": 30},
      {"name": "Bob",   "city": "Boston"}
    ]
    
    CSV:
    name,age,city
    Alice,30,
    Bob,,Boston
    

    Missing keys produce empty cells. The decision: empty string vs literal null. Most CSV consumers treat empty as null, so emitting empty is the more compatible choice.

    Nested objects: flattening

    Pure CSV can’t represent nesting. A flattening strategy turns nested objects into dotted keys:

    JSON:
    [
      {
        "name": "Alice",
        "address": {
          "city": "NYC",
          "zip": "10001"
        }
      }
    ]
    
    CSV (flattened):
    name,address.city,address.zip
    Alice,NYC,10001
    

    Some tools use _ instead of . (address_city); some use [] syntax for arrays (tags[0]). Pick the convention that matches the consumer.

    Arrays of values

    A field that’s an array of primitives:

    JSON:
    {"name": "Alice", "tags": ["vip", "active"]}
    
    CSV options:
    A. Joined string:    name,tags
                          Alice,"vip,active"
    
    B. JSON-encoded:     name,tags
                          Alice,"[""vip"",""active""]"
    
    C. Multiple rows:    name,tag
                          Alice,vip
                          Alice,active
    
    D. Indexed columns:  name,tags[0],tags[1]
                          Alice,vip,active
    

    All four are valid; pick what the consumer expects:

    • A is human-readable but ambiguous if a tag contains a comma.
    • B is unambiguous but the consumer needs to JSON-decode the cell.
    • C denormalizes (one row per array element) — useful for analytics imports.
    • D preserves indexability but assumes a known max length.

    The tool above uses B: an array or nested object is written into its cell as JSON. It has no option for the other layouts.

    Arrays of objects

    Most complex case. A field that’s an array of objects:

    JSON:
    {
      "user": "Alice",
      "purchases": [
        {"item": "Book", "price": 12},
        {"item": "Pen",  "price": 3}
      ]
    }
    

    Three strategies:

    • Flatten with index: user,purchases[0].item,purchases[0].price,purchases[1].item,purchases[1].price. Hideous for arrays of variable length.
    • Denormalize to multiple rows: one row per purchase, with the user repeated. Loses 1:1 row-per-object structure.
    • JSON-encode the field: user,purchases with the purchases column being a JSON string. Compact but requires the consumer to JSON-decode.

    For analytics imports (Looker, Tableau), denormalization is usually the right answer. For round-trippable data interchange, JSON-encoded is best.

    Type handling

    JSON has typed values; CSV has only strings. The conversion has to decide:

    JSON valueCSV cell
    "string"string (or "string" if it contains commas/quotes/newlines)
    4242
    3.143.14
    true / falsetrue / false
    nullempty (or null literal — see below)
    {} (empty object)empty or {}
    [] (empty array)empty or []

    The handling of null is the most variable: empty cell, the literal string null, or NULL are all seen. Most consumers tolerate the empty-cell form; some explicitly distinguish “empty” from “null.”

    CSV escaping

    When a value contains characters meaningful to CSV, the cell must be quoted and any internal quotes doubled:

    JSON valueCSV cell
    AliceAlice
    Smith, Alice"Smith, Alice"
    Said "hi""Said ""hi"""
    Line 1\nLine 2"Line 1\nLine 2" (literal newline inside quotes)

    The tool above quotes any cell that contains ,, ", \n or \r, which is what RFC 4180 requires. Leading and trailing spaces are kept as they are, unquoted.

    Common pitfalls

    • Inconsistent keys across rows. Header is the union; cells with no value are empty. Verify after generation.
    • Excel auto-formatting. Excel sometimes interprets cells like 12-3 as dates or 0123 as the number 123 (dropping the leading zero). For Excel-bound CSVs, prefix problematic cells with ' or use a format Excel won’t reinterpret.
    • Encoding. UTF-8 is the modern default. Excel on Windows historically expected Windows-1252 — opening a UTF-8 CSV in older Excel versions produces mojibake unless the file has a BOM. Some tools add a UTF-8 BOM to “Excel-compatible” CSV; the tool above doesn’t, so in older Excel use Data → From Text/CSV and pick UTF-8 instead of double-clicking the file.
    • Localized number formats. A JSON number 1234.5 becomes 1234.5 in the CSV. Excel in some locales (Germany, France) expects 1234,5 — opening will misparse. Either align with the consumer’s locale or use TSV with the same approach.
    • Lost precision. A 64-bit integer in JSON (12345678901234567890) doesn’t fit in JavaScript’s number type and may lose precision when parsed. The CSV cell may be 1.234567890123457e19 instead of the original digits. For large integers, the JSON consumer needs a big-integer-aware parser.

    For the reverse direction, see CSV to JSON. For other JSON conversions, see JSON to TypeScript, JSON to YAML, JSON to Markdown table.