Intermediate · Exceptions, delegates, events and async · topic 30 of 43 · 30 min
Files and JSON with System.Text.Json
Explanation
Why files and JSON?
Everything in variables disappears when the program ends. To keep data, such as a list of expenses or app settings, you save it to a file. JSON (JavaScript Object Notation) is a simple text format of names and values in curly braces and lists in square brackets. Almost every web API speaks JSON, so learning it serves you everywhere.
{
"name": "Asha",
"city": "Pune",
"marks": [91, 85, 78],
"isActive": true
}Reading and writing text files
string folder = Path.Combine(Environment.CurrentDirectory, "data");
Directory.CreateDirectory(folder); // does nothing if it exists
string file = Path.Combine(folder, "notes.txt");
await File.WriteAllTextAsync(file, "First line\n");
await File.AppendAllTextAsync(file, "Second line\n");
if (File.Exists(file))
{
string[] lines = await File.ReadAllLinesAsync(file);
Console.WriteLine($"{lines.Length} lines; first: {lines[0]}");
}A relative path such as "data/notes.txt" is relative to the current working directory: the folder you ran the program from, which for dotnet run is usually the project folder. If the file "cannot be found" but you can see it, print Environment.CurrentDirectory to check where the program is looking.
Big files: read line by line
int count = 0;
foreach (string line in File.ReadLines("big-log.txt")) // reads lazily, one line at a time
{
if (line.Contains("ERROR")) count++;
}
Console.WriteLine($"{count} errors");Objects to JSON: Serialize
using System.Text.Json;
var expense = new Expense("Groceries", 1240.5m, new DateOnly(2026, 3, 14));
string json = JsonSerializer.Serialize(expense);
Console.WriteLine(json);
// {"Title":"Groceries","Amount":1240.5,"Date":"2026-03-14"}
public record Expense(string Title, decimal Amount, DateOnly Date);JSON to objects: Deserialize
string text = """{"Title":"Rent","Amount":15000,"Date":"2026-04-01"}""";
Expense? e = JsonSerializer.Deserialize<Expense>(text);
Console.WriteLine($"{e?.Title}: {e?.Amount}"); // Rent: 15000Options: indentation and camelCase
Most web APIs use camelCase names ("title", not "Title"). By default, deserializing is case-sensitive, so camelCase JSON would NOT fill your PascalCase properties; they would silently stay empty. Set options to match. Create the options object once and reuse it, because .NET caches work per options instance.
var options = new JsonSerializerOptions
{
WriteIndented = true, // pretty, multi-line output
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // Title -> "title"
PropertyNameCaseInsensitive = true // accept "title" or "Title"
};
string pretty = JsonSerializer.Serialize(expense, options);
Expense? back = JsonSerializer.Deserialize<Expense>(pretty, options);Attributes for special cases
using System.Text.Json.Serialization;
public class Customer
{
[JsonPropertyName("full_name")] public string Name { get; set; } = "";
[JsonIgnore] public string PasswordHash { get; set; } = ""; // never written to JSON
}Saving and loading a list
var list = new List<Expense> { new("Tea", 20m, new DateOnly(2026, 3, 1)) };
await File.WriteAllTextAsync("expenses.json", JsonSerializer.Serialize(list, options));
string saved = await File.ReadAllTextAsync("expenses.json");
var loaded = JsonSerializer.Deserialize<List<Expense>>(saved, options) ?? new List<Expense>();
Console.WriteLine(loaded.Count); // 1Common mistakes and fixes
- camelCase JSON into PascalCase properties without options: values stay default. Use PropertyNameCaseInsensitive = true or JsonSerializerDefaults.Web.
- Saving secrets (passwords, API keys) into JSON files that get committed to Git: never. Use [JsonIgnore] and proper secret storage.
- Hard-coding "C:\Users\..." paths: they break on other machines and Linux. Use Path.Combine and relative or configured paths.
- Forgetting that a file may not exist on first run: check File.Exists and start with an empty list.
- Mixing up System.Text.Json and the older Newtonsoft.Json library: both are popular, but their attributes and options differ. Check which one a project uses.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Files and JSON with System.Text.Json · 1/7
In this lesson your program will save data to files and use JSON.
Prefer a video by a teacher? Find free videos on “C# System.Text.Json serialize deserialize read write file async tutorial” on YouTube ↗
In real life: A personal expense tracker that remembers your data
A student in Kolkata tracks monthly spending in a small console app. Each run loads expenses.json (or starts empty), adds the new expense, prints the month's total, and saves the file back.
using System.Text.Json;
const string FilePath = "expenses.json";
var options = new JsonSerializerOptions { WriteIndented = true, PropertyNameCaseInsensitive = true };
List<Expense> expenses = File.Exists(FilePath)
? JsonSerializer.Deserialize<List<Expense>>(await File.ReadAllTextAsync(FilePath), options) ?? new()
: new();
expenses.Add(new Expense("Metro pass", 500m, DateOnly.FromDateTime(DateTime.Today)));
decimal monthTotal = expenses
.Where(e => e.Date.Month == DateTime.Today.Month && e.Date.Year == DateTime.Today.Year)
.Sum(e => e.Amount);
Console.WriteLine($"{expenses.Count} expenses saved. This month: Rs {monthTotal}");
await File.WriteAllTextAsync(FilePath, JsonSerializer.Serialize(expenses, options));
public record Expense(string Title, decimal Amount, DateOnly Date);Practice (0/4 done)
record P(string Name, int Age); What does JsonSerializer.Serialize(new P("Ravi", 30)) produce with default options?
JSON {"name":"Ravi"} is deserialized into a class with property Name using default options. What is Name?
Which is best for counting lines that contain "ERROR" in a 2 GB log file?
Write a console app that keeps a contacts list in contacts.json. On start, load the list (or start empty if the file is missing), add a contact typed by the user (name and phone), print all contacts, and save the list with indented camelCase JSON.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Events: letting objects notify each otherExceptions, delegates, events and async · same module
- Delegates, lambdas, Func, Action and PredicateExceptions, delegates, events and async · same module
- Serde and JSONRust Programming · Real projects: CLI, web and WebAssembly
- JSON: the language of APIsBackend Development · How the web and backends work
- YAML in depthDevOps: Git, Docker, Kubernetes and CI/CD · Configuration management with Ansible, and YAML
- REST APIs and JSONComputer Networking (CCNA level) · Automation, SDN and cloud networking
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Why use Path.Combine("data", "a.txt") instead of "data\\a.txt"?
Answer: It uses the correct separator on every operating system
Windows uses \ and Linux/macOS use /; Path.Combine handles it.
What does JsonSerializer.Deserialize<T> throw for text that is not valid JSON?
Answer: JsonException
Invalid JSON raises JsonException.
Which option makes the JSON output multi-line and readable?
Answer: WriteIndented
WriteIndented = true pretty-prints the output.
What does [JsonIgnore] do?
Answer: Leaves the property out of the JSON
The property is skipped when serializing and deserializing.
Is System.Text.Json part of .NET or a separate download?
Answer: Built into .NET
System.Text.Json ships with modern .NET.
Which feature must be enabled on serde for #[derive(Serialize)]?Related
What happens when JSON.parse gets broken text?Related
Answer: It throws a SyntaxError
JSON.parse throws on bad input, so wrap it in try and catch.
Which character is NOT allowed for indentation in YAML?Related
Which value is valid JSON?Related
Answer: {"up": true}
JSON needs double quotes, lower-case true and no trailing comma.
From REST APIs and JSON
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Exceptions, delegates, events and async”. The module exam comes next.
- Module 7 exam: Exceptions, delegates, events and async · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Web APIs with ASP.NET Core and EF Core. 43 topics left in the course before the final exam.