How to convert CSV to JSON across languages
Real snippets to convert header-based CSV into JSON across languages. Includes dependencies when CSV parsing is not available in the standard library.
Use this article when...
This section is built for speed: one concrete task, multiple stacks, and the minimum context needed not to copy the wrong thing.
Work from the request or response body that actually failed instead of rebuilding JSON from memory.
The useful path here is fast adaptation: choose your stack, then copy only after reading the notes around parsing and formatting.
Once the snippet runs, inspect the live payload with JSON Formatter or the related tool to confirm the structure is really fixed.
What this article gives you
A short path from incident to implementation: context, language-specific snippets, and the warning signs that usually get skipped.
13
3
csv, json, data, conversion, imports
CSV is still common for exports, quick migrations, and handoff to non-technical teams. The problem starts when you need to inspect records, validate columns, or turn that data into payloads your scripts and APIs can actually use.
Converting CSV to JSON is not just a format swap. It makes row shape, empty values, duplicate headers, and typing assumptions visible before they break an import.
Dependency snapshot by language
Check this first to see which snippets use built-in/runtime APIs and which ones need external packages before you start copying code.
Built-in or runtime API
CsvHelper.
Built-in or runtime API
Built-in or runtime API
Review the notes for setup details.
csv-parse.
Review the notes for setup details.
Built-in or runtime API
Built-in or runtime API
Built-in or runtime API
Review the notes for setup details.
csv-parse.
Built-in or runtime API
How to use these code examples well
The snippet is only the fast path. The explanation and notes are what keep you from copying the wrong assumption into production.
Make sure the article really matches the issue you are debugging before copying code just because the title looks close.
That is where portability limits, dependency requirements, and runtime assumptions usually appear.
If the code runs but the bug remains, jump to the related tool and inspect the real payload, token, timestamp, or config.
Code examples by language
Snippets that compile/run, plus short explanations and real-world notes.
Bash
python3 - <<'PY'
import csv
import json
from io import StringIO
csv_text = """id,name,active
1,Ana,true
2,Luis,false
"""
rows = list(csv.DictReader(StringIO(csv_text)))
print(json.dumps(rows, indent=2, ensure_ascii=False))
PY
If Python is already available, this is a practical terminal path: DictReader uses the first row as headers and json.dumps produces readable output.
- Uses only the standard library.
- Values remain strings unless you add type inference afterwards.
C#
using System.Globalization;
using System.Linq;
using System.Text.Json;
using CsvHelper;
using CsvHelper.Configuration;
var csvText = """
id,name,active
1,Ana,true
2,Luis,false
""";
using var textReader = new StringReader(csvText);
using var csv = new CsvReader(textReader, new CsvConfiguration(CultureInfo.InvariantCulture));
var rows = csv.GetRecords<dynamic>().ToList();
Console.WriteLine(JsonSerializer.Serialize(rows, new JsonSerializerOptions { WriteIndented = true }));
CsvHelper handles CSV parsing robustly and lets you serialize the result directly to JSON with System.Text.Json.
- Dependency: CsvHelper.
- Good fit for import jobs and internal .NET utilities.
Delphi
program CsvToJson;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Classes,
System.JSON;
function ParseLine(const Line: string): TStringList;
begin
Result := TStringList.Create;
Result.StrictDelimiter := True;
Result.Delimiter := ',';
Result.DelimitedText := Line;
end;
var
Lines, Headers, Values: TStringList;
JsonRows: TJSONArray;
JsonRow: TJSONObject;
I, J: Integer;
begin
Lines := TStringList.Create;
JsonRows := TJSONArray.Create;
try
Lines.Text := 'id,name,active' + sLineBreak + '1,Ana,true' + sLineBreak + '2,Luis,false';
Headers := ParseLine(Lines[0]);
try
for I := 1 to Lines.Count - 1 do
begin
Values := ParseLine(Lines[I]);
try
JsonRow := TJSONObject.Create;
for J := 0 to Headers.Count - 1 do
JsonRow.AddPair(Headers[J], Values[J]);
JsonRows.AddElement(JsonRow);
finally
Values.Free;
end;
end;
Writeln(JsonRows.Format(2));
finally
Headers.Free;
end;
finally
JsonRows.Free;
Lines.Free;
end;
end.
In Delphi you can handle simple CSV with TStringList.DelimitedText and build the final JSON using System.JSON without external dependencies.
- Standard library only.
- Good for simple CSV; if you need multiline fields or harder edge cases, use a dedicated CSV parser.
Go
package main
import (
"encoding/csv"
"encoding/json"
"fmt"
"strings"
)
func main() {
input := "id,name,active
1,Ana,true
2,Luis,false
"
reader := csv.NewReader(strings.NewReader(input))
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
headers := records[0]
rows := make([]map[string]string, 0, len(records)-1)
for _, record := range records[1:] {
row := make(map[string]string, len(headers))
for index, header := range headers {
row[header] = record[index]
}
rows = append(rows, row)
}
out, _ := json.MarshalIndent(rows, "", " ")
fmt.Println(string(out))
}
encoding/csv and encoding/json cover the usual case with no external dependencies. Enough for CLIs and simple operational transforms.
- Standard library only.
- If columns may be missing or extra, validate field counts before indexing rows.
Java
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import java.util.Map;
public class Main {
public static void main(String[] args) throws Exception {
String input = """
id,name,active
1,Ana,true
2,Luis,false
""";
CsvMapper csvMapper = new CsvMapper();
CsvSchema schema = CsvSchema.emptySchema().withHeader();
MappingIterator<Map<String, String>> rows =
csvMapper.readerFor(new TypeReference<Map<String, String>>() {}).with(schema).readValues(input);
ObjectMapper jsonMapper = new ObjectMapper();
System.out.println(jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rows.readAll()));
}
}
Jackson CSV lets you parse the header and obtain rows as maps without maintaining a manual parser in Java.
- Dependencies: jackson-databind and jackson-dataformat-csv.
- Useful when CSV is already part of a backend pipeline in Java.
JavaScript
import { parse } from 'csv-parse/sync';
const input = [
'id,name,active',
'1,Ana,true',
'2,Luis,false'
].join('
');
const rows = parse(input, {
columns: true,
skip_empty_lines: true
});
console.log(JSON.stringify(rows, null, 2));
In Node.js it is better to rely on a real CSV parser. csv-parse handles quotes, embedded commas, and common edge cases much better than manual splitting.
- Dependency: csv-parse.
- columns: true uses the first row as headers automatically.
Kotlin
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.dataformat.csv.CsvMapper
import com.fasterxml.jackson.dataformat.csv.CsvSchema
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
fun main() {
val input = """
id,name,active
1,Ana,true
2,Luis,false
""".trimIndent()
val csvMapper = CsvMapper()
val schema = CsvSchema.emptySchema().withHeader()
val rows = csvMapper
.readerFor(object : TypeReference<Map<String, String>>() {})
.with(schema)
.readValues<Map<String, String>>(input)
.readAll()
println(jacksonObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(rows))
}
In Kotlin/JVM you can reuse Jackson CSV just like in Java and still produce readable JSON with very little extra code.
- Dependencies: jackson-dataformat-csv and jackson-module-kotlin.
- A good option for internal JVM tooling.
PHP
<?php
$csv = <<<CSV
id,name,active
1,Ana,true
2,Luis,false
CSV;
$handle = fopen('php://temp', 'r+');
fwrite($handle, $csv);
rewind($handle);
$headers = fgetcsv($handle);
$rows = [];
while (($values = fgetcsv($handle)) !== false) {
$rows[] = array_combine($headers, $values);
}
fclose($handle);
echo json_encode($rows, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . PHP_EOL;
fgetcsv already solves CSV parsing so you do not have to invent your own parser. Using a temporary stream lets you reuse the native API even when the CSV arrives as a string.
- Standard functions only.
- array_combine assumes each row has the same number of columns as the header.
Python
import csv
import json
from io import StringIO
csv_text = """id,name,active
1,Ana,true
2,Luis,false
"""
reader = csv.DictReader(StringIO(csv_text))
rows = list(reader)
print(json.dumps(rows, indent=2, ensure_ascii=False))
csv.DictReader is probably the most direct way in Python to convert header-based rows into JSON objects.
- Standard library only.
- As in many stacks, type inference is still your decision after parsing.
Ruby
require 'csv'
require 'json'
csv_text = <<~CSV
id,name,active
1,Ana,true
2,Luis,false
CSV
rows = CSV.parse(csv_text, headers: true).map(&:to_h)
puts JSON.pretty_generate(rows)
Ruby ships with CSV and JSON in the standard library, so you can move from export to readable objects with very little code.
- Standard library only.
- headers: true maps each row to a hash using the first line.
Rust
use csv::ReaderBuilder;
use serde_json::{Map, Value};
fn main() {
let input = "id,name,active
1,Ana,true
2,Luis,false
";
let mut reader = ReaderBuilder::new().from_reader(input.as_bytes());
let headers = reader.headers().unwrap().clone();
let rows: Vec<Value> = reader
.records()
.map(|record| {
let record = record.unwrap();
let mut object = Map::new();
for (header, value) in headers.iter().zip(record.iter()) {
object.insert(header.to_string(), Value::String(value.to_string()));
}
Value::Object(object)
})
.collect();
println!("{}", serde_json::to_string_pretty(&rows).unwrap());
}
The csv crate handles parsing and serde_json lets you build a clean JSON output for inspection or handoff.
- Dependencies: csv = "1", serde_json = "1".
- If you want type inference, do it before inserting values into the JSON.
TypeScript
import { parse } from 'csv-parse/sync';
const input = [
'id,name,active',
'1,Ana,true',
'2,Luis,false'
].join('
');
const rows = parse(input, {
columns: true,
skip_empty_lines: true
}) as Array<Record<string, string>>;
console.log(JSON.stringify(rows, null, 2));
The TypeScript pattern is almost identical to Node.js, but with explicit typing to make the row shape clear.
- Dependency: csv-parse.
- If you normalize types afterwards, type the final result against your real contract.
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Text.Json
Imports Microsoft.VisualBasic.FileIO
Module Program
Sub Main()
Dim csvText = "id,name,active" & Environment.NewLine &
"1,Ana,true" & Environment.NewLine &
"2,Luis,false"
Dim rows As New List(Of Dictionary(Of String, String))()
Using parser As New TextFieldParser(New StringReader(csvText))
parser.TextFieldType = FieldType.Delimited
parser.SetDelimiters(",")
Dim headers = parser.ReadFields()
While Not parser.EndOfData
Dim fields = parser.ReadFields()
Dim row As New Dictionary(Of String, String)()
For i = 0 To headers.Length - 1
row(headers(i)) = fields(i)
Next
rows.Add(row)
End While
End Using
Console.WriteLine(JsonSerializer.Serialize(rows, New JsonSerializerOptions With {.WriteIndented = True}))
End Sub
End Module
TextFieldParser handles delimited CSV in VB.NET and lets you turn each row into a dictionary before serializing it to JSON with System.Text.Json.
- Only the .NET/VB runtime.
- Usually handles quotes and delimiters better than manual splitting.
Related tools
Use the tool when you need to generate or validate output fast.
Trust and privacy
Copy/paste-friendly by design. Still, treat real production secrets carefully.
- Prefer synthetic examples when sharing screens or recording.
- Do not paste real API keys, tokens, or personal data unless you fully trust your environment.
- If you need ordering, do not use UUID v4 as a sortable id: add createdAt or use sortable ids.