Overview
When retrieving data from a Web API, the traditional two-step process of “downloading as a string and then deserializing” can be shortened into a single step using the GetFromJsonAsync method. By utilizing System.Net.Http.Json, which was standardized in .NET 5 and later, you can map external data into objects in a type-safe and concise manner.
Specifications (Input/Output)
- Input: The URL of a Web API delivering JSON data.
- Output: A C# instance mapped with the JSON data, with its properties displayed in the console.
- Prerequisites: .NET 5.0 or higher (for lower versions, the
System.Net.Http.JsonNuGet package is required).
Basic Usage
Call the GetFromJsonAsync<T> extension method of HttpClient. Specify a class for the generic type argument T that matches the JSON structure.
// Retrieve data from Web API and convert it to the Person type
Person? person = await client.GetFromJsonAsync<Person>("https://api.example.com/users/1");
if (person != null)
{
Console.WriteLine(person.Name);
}
Full Code Example
The following example is a console application that retrieves and displays information for a specific currency pair from a “Currency Exchange Rate API.”
using System;
using System.Net.Http;
using System.Net.Http.Json; // Required
using System.Text.Json.Serialization;
using System.Threading.Tasks;
class Program
{
// Maintain HttpClient as static to reuse the instance
private static readonly HttpClient _httpClient = new HttpClient();
static async Task Main()
{
// Placeholder endpoint for an exchange rate API
string apiUrl = "https://api.finance-service.net/v1/rates/usd-jpy";
Console.WriteLine($"Retrieving data from: {apiUrl} ...");
try
{
// Send GET request and deserialize the response body (JSON) into the ExchangeRate type
// Internally uses System.Text.Json
ExchangeRate? rateData = await _httpClient.GetFromJsonAsync<ExchangeRate>(apiUrl);
if (rateData != null)
{
Console.WriteLine("--- Retrieval Results ---");
Console.WriteLine($"Currency Pair: {rateData.BaseCurrency} / {rateData.TargetCurrency}");
Console.WriteLine($"Rate : {rateData.Rate}");
Console.WriteLine($"Last Updated : {rateData.LastUpdated}");
}
else
{
Console.WriteLine("Data was null.");
}
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Communication Error: {ex.Message}");
if (ex.StatusCode.HasValue)
{
Console.WriteLine($"Status Code: {ex.StatusCode}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected Error: {ex.Message}");
}
}
}
// Class definition corresponding to JSON data
// Example JSON: { "base": "USD", "target": "JPY", "current_rate": 145.50, "updated_at": "2026-01-16T10:00:00" }
public class ExchangeRate
{
// Map the JSON property name "base" to "BaseCurrency"
[JsonPropertyName("base")]
public string BaseCurrency { get; set; } = string.Empty;
[JsonPropertyName("target")]
public string TargetCurrency { get; set; } = string.Empty;
[JsonPropertyName("current_rate")]
public decimal Rate { get; set; }
[JsonPropertyName("updated_at")]
public DateTime LastUpdated { get; set; }
}
Customization Points
- Specifying Options: By passing
JsonSerializerOptionsas the second argument toGetFromJsonAsync, you can change behaviors such as automatic camelCase conversion or case-insensitivity settings.C#var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var data = await client.GetFromJsonAsync<MyData>(url, options); - Timeout Settings: To control the timeout for the entire process (including both communication and deserialization), use the overload that accepts a
CancellationToken.
Important Notes
- Exception Handling: In addition to communication errors (like 404 or 500), a
JsonExceptionwill occur if the JSON format does not match the class definition (e.g., type mismatch). A robust application should consider both types of exceptions. - Nullability: If there is a possibility that the API returns
null, it is safer to receive the return value asT?(nullable type). - Character Encoding:
System.Net.Http.Jsonassumes UTF-8 encoding by default. If you are dealing with APIs using different encodings, you will need to retrieve the byte array viaGetAsyncand convert it manually.
Advanced Application
Batch Retrieving JSON Arrays (Lists)
If the API returns an array [...] instead of a single object, specify List<T> or an array type.
// API return: [{ "id": 1, "name": "A" }, { "id": 2, "name": "B" }]
List<User>? users = await _httpClient.GetFromJsonAsync<List<User>>("https://api.example.com/users");
if (users != null)
{
foreach (var user in users)
{
Console.WriteLine($"ID: {user.Id}, Name: {user.Name}");
}
}
Conclusion
By utilizing GetFromJsonAsync, you can simplify the common pattern of HTTP communication and JSON parsing into an extremely straightforward implementation. This improves code readability and reduces boilerplate code, making it the primary choice for utilizing Web APIs in modern C# development. However, since inconsistencies in JSON structure due to API specification changes can lead to exceptions, ensure that error handling is properly implemented.
