.NET JSON Handling
Introduction
JSON (JavaScript Object Notation) is a lightweight data interchange format that has become the standard for data exchange in web applications and APIs. In .NET applications, working with JSON is a common requirement for tasks such as:
- Consuming REST APIs
- Storing configuration data
- Persisting application state
- Data interchange between different systems
This guide will walk you through handling JSON in .NET applications using both the built-in System.Text.Json
namespace (available since .NET Core 3.0) and the popular third-party library Newtonsoft.Json
(Json.NET). By the end, you'll be comfortable reading, writing, and manipulating JSON data in your .NET applications.
Getting Started with JSON in .NET
What is JSON?
JSON is a text-based data format consisting of key-value pairs and arrays, similar to dictionaries and lists in C#. A simple JSON object looks like this:
{
"name": "John Doe",
"age": 30,
"isStudent": false,
"grades": [85, 90, 78],
"address": {
"street": "123 Main St",
"city": "Anytown"
}
}
JSON Libraries in .NET
In .NET, you have two main options for working with JSON:
- System.Text.Json: Built-in to the .NET runtime (since .NET Core 3.0)
- Newtonsoft.Json: A popular third-party library (also called Json.NET)
We'll cover both in this guide.
Using System.Text.Json
The System.Text.Json
namespace provides modern, high-performance JSON APIs that are built into the .NET SDK.
Setup
If you're using .NET Core 3.0 or later, System.Text.Json
is already included. For older versions, you need to install the NuGet package:
dotnet add package System.Text.Json
JSON Serialization (Object to JSON)
Serialization converts C# objects to JSON strings. Here's a basic example:
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
// Create an object
Person person = new Person
{
Name = "John Doe",
Age = 30,
IsStudent = false
};
// Serialize to JSON
string jsonString = JsonSerializer.Serialize(person);
Console.WriteLine(jsonString);
// Output: {"Name":"John Doe","Age":30,"IsStudent":false}
Pretty Printing JSON
For more readable JSON output, you can use serialization options:
var options = new JsonSerializerOptions
{
WriteIndented = true
};
string prettyJson = JsonSerializer.Serialize(person, options);
Console.WriteLine(prettyJson);
/* Output:
{
"Name": "John Doe",
"Age": 30,
"IsStudent": false
}
*/
JSON Deserialization (JSON to Object)
Deserialization converts JSON strings back to C# objects:
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
// JSON string
string jsonString = @"{""Name"":""Jane Smith"",""Age"":25,""IsStudent"":true}";
// Deserialize JSON to object
Person person = JsonSerializer.Deserialize<Person>(jsonString);
// Access object properties
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}, Student: {person.IsStudent}");
// Output: Name: Jane Smith, Age: 25, Student: True
Reading and Writing JSON Files
Reading JSON from files and writing JSON to files is a common task in .NET applications:
using System;
using System.IO;
using System.Text.Json;
public class AppConfig
{
public string DatabaseConnectionString { get; set; }
public int MaxConnections { get; set; }
public bool EnableLogging { get; set; }
}
// Create config object
AppConfig config = new AppConfig
{
DatabaseConnectionString = "Server=localhost;Database=MyApp;User Id=admin;Password=password123;",
MaxConnections = 100,
EnableLogging = true
};
// Serialize and save to file
string jsonString = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText("appconfig.json", jsonString);
Console.WriteLine("Config saved to file!");
// Read from file and deserialize
string jsonFromFile = File.ReadAllText("appconfig.json");
AppConfig loadedConfig = JsonSerializer.Deserialize<AppConfig>(jsonFromFile);
Console.WriteLine($"Connection string: {loadedConfig.DatabaseConnectionString}");
Console.WriteLine($"Max connections: {loadedConfig.MaxConnections}");
Console.WriteLine($"Logging enabled: {loadedConfig.EnableLogging}");
Working with Complex Objects
Often, you'll need to work with nested objects and arrays. System.Text.Json
handles this seamlessly:
using System;
using System.Collections.Generic;
using System.Text.Json;
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public List<int> Grades { get; set; }
public Address HomeAddress { get; set; }
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
// Create complex object
Student student = new Student
{
Name = "Alice Johnson",
Age = 22,
Grades = new List<int> { 95, 87, 91 },
HomeAddress = new Address
{
Street = "456 Oak Avenue",
City = "Boston",
ZipCode = "02108"
}
};
// Serialize to JSON
var options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(student, options);
Console.WriteLine(json);
// Deserialize back to object
Student deserializedStudent = JsonSerializer.Deserialize<Student>(json);
Console.WriteLine($"Student: {deserializedStudent.Name}");
Console.WriteLine($"City: {deserializedStudent.HomeAddress.City}");
Console.WriteLine($"First grade: {deserializedStudent.Grades[0]}");
Customizing JSON Serialization
You can customize the serialization process with attributes and options:
Using JsonPropertyName
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
public class Product
{
[JsonPropertyName("product_id")]
public int Id { get; set; }
[JsonPropertyName("product_name")]
public string Name { get; set; }
[JsonPropertyName("unit_price")]
public decimal Price { get; set; }
[JsonIgnore]
public string InternalCode { get; set; }
}
// Create product
Product product = new Product
{
Id = 1001,
Name = "Laptop",
Price = 1299.99m,
InternalCode = "LPT-1001" // This will be ignored during serialization
};
string json = JsonSerializer.Serialize(product, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);
/* Output:
{
"product_id": 1001,
"product_name": "Laptop",
"unit_price": 1299.99
}
*/
Case Conversion Options
using System;
using System.Text.Json;
public class User
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
}
User user = new User
{
UserId = 1,
FirstName = "John",
LastName = "Doe",
EmailAddress = "[email protected]"
};
// Using camelCase for property names
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);
/* Output:
{
"userId": 1,
"firstName": "John",
"lastName": "Doe",
"emailAddress": "[email protected]"
}
*/
Using Newtonsoft.Json (Json.NET)
While System.Text.Json
is the modern built-in option, many projects still use the popular Newtonsoft.Json library, which offers additional features and flexibility.
Setup
Install the Newtonsoft.Json NuGet package:
dotnet add package Newtonsoft.Json
Basic Serialization and Deserialization
using Newtonsoft.Json;
using System;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
// Create an object
Person person = new Person
{
Name = "John Doe",
Age = 30,
IsStudent = false
};
// Serialize to JSON
string jsonString = JsonConvert.SerializeObject(person, Formatting.Indented);
Console.WriteLine(jsonString);
// Deserialize from JSON
string inputJson = @"{""Name"":""Jane Smith"",""Age"":25,""IsStudent"":true}";
Person deserializedPerson = JsonConvert.DeserializeObject<Person>(inputJson);
Console.WriteLine($"Deserialized: {deserializedPerson.Name}, {deserializedPerson.Age} years old");
Working with JObject for Dynamic JSON
One of Newtonsoft.Json's strengths is working with dynamic JSON where the structure might not be known at compile time:
using Newtonsoft.Json.Linq;
using System;
// Parse JSON string into JObject
string jsonString = @"{
""name"": ""John Smith"",
""age"": 42,
""address"": {
""street"": ""123 Main St"",
""city"": ""New York"",
""zipcode"": ""10001""
},
""phoneNumbers"": [
""212-555-1234"",
""646-555-4567""
]
}";
JObject jsonObj = JObject.Parse(jsonString);
// Access properties directly
string name = (string)jsonObj["name"];
int age = (int)jsonObj["age"];
string street = (string)jsonObj["address"]["street"];
string firstPhone = (string)jsonObj["phoneNumbers"][0];
Console.WriteLine($"Name: {name}");
Console.WriteLine($"Age: {age}");
Console.WriteLine($"Street: {street}");
Console.WriteLine($"First phone: {firstPhone}");
// Modify properties
jsonObj["age"] = 43;
jsonObj["address"]["zipcode"] = "10002";
((JArray)jsonObj["phoneNumbers"]).Add("212-555-8901");
// Convert back to string
string modifiedJson = jsonObj.ToString();
Console.WriteLine("\nModified JSON:");
Console.WriteLine(modifiedJson);
LINQ to JSON
Newtonsoft.Json provides LINQ capabilities for querying JSON data:
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
string jsonArray = @"[
{ ""id"": 1, ""name"": ""Apple"", ""price"": 0.99, ""category"": ""Fruit"" },
{ ""id"": 2, ""name"": ""Bread"", ""price"": 2.49, ""category"": ""Bakery"" },
{ ""id"": 3, ""name"": ""Milk"", ""price"": 1.79, ""category"": ""Dairy"" },
{ ""id"": 4, ""name"": ""Orange"", ""price"": 0.79, ""category"": ""Fruit"" },
{ ""id"": 5, ""name"": ""Cheese"", ""price"": 3.99, ""category"": ""Dairy"" }
]";
JArray products = JArray.Parse(jsonArray);
// Query for all dairy products
var dairyProducts = products.Where(p => (string)p["category"] == "Dairy");
Console.WriteLine("Dairy Products:");
foreach (var product in dairyProducts)
{
Console.WriteLine($"- {product["name"]}: ${product["price"]}");
}
// Find products with price less than $1
var affordableProducts = from p in products
where (decimal)p["price"] < 1
select new {
Name = (string)p["name"],
Price = (decimal)p["price"]
};
Console.WriteLine("\nAffordable Products (under $1):");
foreach (var product in affordableProducts)
{
Console.WriteLine($"- {product.Name}: ${product.Price}");
}
Real-World Example: Working with a REST API
Here's a comprehensive example that shows how to interact with a JSON REST API:
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
public class TodoItem
{
public int Id { get; set; }
public int UserId { get; set; }
public string Title { get; set; }
public bool Completed { get; set; }
}
public class ApiService
{
private readonly HttpClient _httpClient;
public ApiService()
{
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
}
public async Task<List<TodoItem>> GetTodosAsync()
{
// Get JSON data from API
var response = await _httpClient.GetAsync("todos");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
// Deserialize JSON array to list of objects
var todos = JsonSerializer.Deserialize<List<TodoItem>>(content);
return todos;
}
public async Task<TodoItem> CreateTodoAsync(TodoItem newTodo)
{
// Serialize object to JSON
var todoJson = JsonSerializer.Serialize(newTodo);
var content = new StringContent(todoJson, System.Text.Encoding.UTF8, "application/json");
// POST to API
var response = await _httpClient.PostAsync("todos", content);
response.EnsureSuccessStatusCode();
// Read and deserialize response
var responseContent = await response.Content.ReadAsStringAsync();
var createdTodo = JsonSerializer.Deserialize<TodoItem>(responseContent);
return createdTodo;
}
}
// Example usage
public class Program
{
public static async Task Main(string[] args)
{
var apiService = new ApiService();
try
{
// Get todos
Console.WriteLine("Fetching todos...");
var todos = await apiService.GetTodosAsync();
Console.WriteLine($"Received {todos.Count} todos");
// Display first 5 todos
for (int i = 0; i < Math.Min(5, todos.Count); i++)
{
Console.WriteLine($"- {todos[i].Id}: {todos[i].Title} (Completed: {todos[i].Completed})");
}
// Create a new todo
var newTodo = new TodoItem
{
UserId = 1,
Title = "Learn JSON in .NET",
Completed = false
};
Console.WriteLine("\nCreating new todo...");
var createdTodo = await apiService.CreateTodoAsync(newTodo);
Console.WriteLine("Todo created:");
Console.WriteLine($"ID: {createdTodo.Id}");
Console.WriteLine($"Title: {createdTodo.Title}");
Console.WriteLine($"Completed: {createdTodo.Completed}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
Best Practices for JSON Handling in .NET
-
Use DTOs (Data Transfer Objects): Create specific classes for serialization/deserialization to decouple your internal models from external representations.
-
Handle Errors Gracefully: Use try-catch blocks when parsing JSON to handle malformed data.
try
{
var result = JsonSerializer.Deserialize<MyClass>(jsonString);
}
catch (JsonException ex)
{
Console.WriteLine($"Invalid JSON format: {ex.Message}");
}
-
Be Conscious of Performance: For large JSON documents, consider using
JsonDocument
for streaming scenarios to avoid loading the entire document into memory. -
Use Appropriate Naming Conventions: Configure naming policies to match the expected JSON format (camelCase for JavaScript clients, snake_case for some APIs).
-
Use Nullable Reference Types: In .NET Core 3.0+, use nullable reference types to indicate which properties could be null in the JSON.
public class Person
{
public string Name { get; set; } = null!; // Required
public string? MiddleName { get; set; } // Optional
public int Age { get; set; }
}
Choosing Between System.Text.Json and Newtonsoft.Json
-
Use System.Text.Json when:
- Building new .NET Core 3.0+ applications
- Performance is critical
- Basic JSON functionality is sufficient
-
Use Newtonsoft.Json when:
- Working with legacy .NET Framework applications
- You need advanced features (LINQ to JSON, dynamic parsing, etc.)
- You require more customization options
Summary
In this guide, we've explored comprehensive techniques for handling JSON data in .NET applications. We've covered:
- Basic serialization and deserialization with both
System.Text.Json
andNewtonsoft.Json
- Working with complex nested objects and arrays
- Reading from and writing JSON to files
- Customizing JSON serialization and deserialization
- Working with dynamic JSON using JObject and JArray
- Interacting with REST APIs that use JSON
- Best practices for JSON handling
JSON is a fundamental skill for modern .NET development, especially when building web applications, APIs, or any system that communicates with external services. With the knowledge gained from this guide, you should be well-equipped to handle JSON data effectively in your .NET applications.
Exercises
-
Create a console application that reads a JSON file containing a list of products, filters products by category, and writes the filtered list to a new JSON file.
-
Build a simple weather application that consumes a free weather API (like OpenWeatherMap) and deserializes the JSON response to display current weather conditions.
-
Modify the TodoItem example to implement CRUD operations (Create, Read, Update, Delete) against the JSONPlaceholder API.
-
Create a JSON configuration system that loads application settings from a JSON file and allows updating settings at runtime.
Additional Resources
- Official Microsoft System.Text.Json Documentation
- Newtonsoft.Json Documentation
- JSONPlaceholder - Free fake API for testing
- JSON Schema - For validating JSON data structures
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)