Files and JSON
Read and write files, and convert objects to and from JSON with System.Text.Json.
12 min read
You will learn
- Read and write files with File and streams
- Build paths portably with Path
- Serialize and deserialize JSON with System.Text.Json
- Control JSON naming and formatting with options and attributes
Files
System.IO covers everyday file work with simple static methods:
string path = Path.Combine(AppContext.BaseDirectory, "data", "notes.txt");
await File.WriteAllTextAsync(path, "Hello"); // create or overwrite
await File.AppendAllTextAsync(path, "\nAnother line");
string text = await File.ReadAllTextAsync(path);
string[] lines = await File.ReadAllLinesAsync(path);
bool exists = File.Exists(path);
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
foreach (var file in Directory.EnumerateFiles("data", "*.json")) { ... }- Build paths with
Path.Combine— it picks the right separator for Windows, Linux or macOS. - Prefer the async versions in servers.
- For big files, don't load everything into memory: stream them.
await using var stream = File.OpenRead("huge-log.txt");
using var reader = new StreamReader(stream);
while (await reader.ReadLineAsync() is { } line)
{
if (line.Contains("ERROR")) Console.WriteLine(line);
}
// Or simply — lazily, one line at a time:
foreach (var line in File.ReadLines("huge-log.txt")) { ... }Files on servers
Remember the stateless rule: a web server's local disk may disappear with the instance. Store uploads and generated files in object storage (Azure Blob Storage, Amazon S3), and data in a database.
JSON with System.Text.Json
JSON is the lingua franca of web APIs and config files. .NET's built-in System.Text.Json converts objects to JSON (serialization) and back (deserialization):
using System.Text.Json;
record Product(int Id, string Name, decimal Price, string[] Tags);
var product = new Product(1, "Laptop", 999m, ["electronics", "sale"]);
string json = JsonSerializer.Serialize(product);
// {"Id":1,"Name":"Laptop","Price":999,"Tags":["electronics","sale"]}
Product? back = JsonSerializer.Deserialize<Product>(json);Options
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web) // camelCase + case-insensitive
{
WriteIndented = true, // pretty-print
};
string pretty = JsonSerializer.Serialize(product, options);
// {
// "id": 1,
// "name": "Laptop",
// ...Reuse options instances — creating them is relatively expensive. ASP.NET Core uses web defaults automatically for request and response bodies.
Attributes
public class User
{
[JsonPropertyName("user_name")] // custom JSON name
public string Name { get; set; } = "";
[JsonIgnore] // never serialized
public string PasswordHash { get; set; } = "";
[JsonConverter(typeof(JsonStringEnumConverter))]
public Role Role { get; set; } // "Admin" instead of 1
}When JSON doesn't match
- Unknown JSON properties are ignored by default.
- Missing properties keep their default values (or fail if marked
required). - Malformed JSON throws
JsonException— catch it when the input comes from outside.
Reading JSON without a class
For quick inspection of arbitrary JSON, use the DOM types:
using var doc = JsonDocument.Parse(json);
string name = doc.RootElement.GetProperty("name").GetString()!;Try it
C# runtime not loaded
Key takeaways
FileandPathcover everyday file I/O; stream large files; use async in servers.JsonSerializer.Serialize/Deserialize<T>convert between objects and JSON.JsonSerializerDefaults.Webgives camelCase, case-insensitive JSON — reuse options instances.- Customize with
[JsonPropertyName],[JsonIgnore], converters; catchJsonExceptionfor untrusted input.
Exercise
Using System.Text.Json: ToJson serializes an Order with web conventions (camelCase, compact). FromJson reads an Order back (property names in any case) and returns null for invalid JSON. TotalFromJson reads a JSON array of items and returns the sum of price × qty.
C# runtime not loaded
Hint 1
new JsonSerializerOptions(JsonSerializerDefaults.Web) gives camelCase names and case-insensitive reading — keep one in a static field or local variable and pass it to Serialize/Deserialize.
Hint 2
Invalid JSON throws JsonException — catch it and return null.
Hint 3
Deserialize the array with JsonSerializer.Deserialize<List<Item>>(json, options), then Sum(i => i.Price * i.Qty).
Show solution
using System.Text.Json;
Console.WriteLine(ToJson(new Order(1, "Ada", 9.5m)));
JsonSerializerOptions Options() => new(JsonSerializerDefaults.Web);
string ToJson(Order order) => JsonSerializer.Serialize(order, Options());
Order? FromJson(string json)
{
try
{
return JsonSerializer.Deserialize<Order>(json, Options());
}
catch (JsonException)
{
return null;
}
}
decimal TotalFromJson(string json)
{
var items = JsonSerializer.Deserialize<List<Item>>(json, Options()) ?? [];
return items.Sum(i => i.Price * i.Qty);
}
record Order(int Id, string Customer, decimal Total);
record Item(string Name, decimal Price, int Qty);Quiz
4 questions · 3 correct to pass1.Why use Path.Combine("data", "orders.json") instead of "data/orders.json"?
2.What does JsonSerializerDefaults.Web change?
3.How do you give one property a different JSON name?
4.Why prefer File.ReadAllTextAsync over File.ReadAllText in a web server?
To complete this lesson
- ○ Solve the exercise
- ○ Pass the quiz (3/4 correct)