Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This tutorial builds an app that issues HTTP requests to a REST service on GitHub. The app reads information in JSON format and converts the JSON into C# objects. Converting from JSON to C# objects is known as deserialization.
The tutorial shows how to:
- Send HTTP requests.
- Deserialize JSON responses.
- Configure deserialization with attributes.
If you prefer to follow along with the final sample for this tutorial, you can download it. For download instructions, see Samples and Tutorials.
Prerequisites
- The latest .NET SDK
- Visual Studio Code editor
- The C# DevKit
Create the client app
- Open a command prompt and create a new directory for your app. Make that the current directory. 
- Enter the following command in a console window: - dotnet new console --name WebAPIClient- This command creates the starter files for a basic "Hello World" app. The project name is "WebAPIClient". 
- Navigate into the "WebAPIClient" directory, and run the app. - cd WebAPIClient- dotnet run- dotnet runautomatically runs- dotnet restoreto restore any dependencies that the app needs. It also runs- dotnet buildif needed. You should see the app output- "Hello, World!". In your terminal, press Ctrl+C to stop the app.
Make HTTP requests
This app calls the GitHub API to get information about the projects under the .NET Foundation umbrella. The endpoint is https://api.github.com/orgs/dotnet/repos. To retrieve information, it makes an HTTP GET request. Browsers also make HTTP GET requests, so you can paste that URL into your browser address bar to see what information you'll be receiving and processing.
Use the HttpClient class to make HTTP requests. HttpClient supports only async methods for its long-running APIs. So the following steps create an async method and call it from the Main method.
- Open the - Program.csfile in your project directory and replace its contents with the following:- await ProcessRepositoriesAsync(); static async Task ProcessRepositoriesAsync(HttpClient client) { }- This code: - Replaces the Console.WriteLinestatement with a call toProcessRepositoriesAsyncthat uses theawaitkeyword.
- Defines an empty ProcessRepositoriesAsyncmethod.
 
- Replaces the 
- In the - Programclass, use an HttpClient to handle requests and responses, by replacing the content with the following C#.- using System.Net.Http.Headers; using HttpClient client = new(); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json")); client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter"); await ProcessRepositoriesAsync(client); static async Task ProcessRepositoriesAsync(HttpClient client) { }- This code: - Sets up HTTP headers for all requests:
- An Acceptheader to accept JSON responses
- A User-Agentheader. These headers are checked by the GitHub server code and are necessary to retrieve information from GitHub.
 
- An 
 
- Sets up HTTP headers for all requests:
- In the - ProcessRepositoriesAsyncmethod, call the GitHub endpoint that returns a list of all repositories under the .NET foundation organization:- static async Task ProcessRepositoriesAsync(HttpClient client) { var json = await client.GetStringAsync( "https://api.github.com/orgs/dotnet/repos"); Console.Write(json); }- This code: - Awaits the task returned from calling HttpClient.GetStringAsync(String) method. This method sends an HTTP GET request to the specified URI. The body of the response is returned as a String, which is available when the task completes.
- The response string jsonis printed to the console.
 
- Build the app and run it. - dotnet run- There is no build warning because the - ProcessRepositoriesAsyncnow contains an- awaitoperator. The output is a long display of JSON text.
Deserialize the JSON Result
The following steps simplify the approach to fetching the data and processing it. You will use the GetFromJsonAsync extension method that's part of the 📦 System.Net.Http.Json NuGet package to fetch and deserialize the JSON results into objects.
- Create a file named Repository.cs and add the following code: - public record class Repository(string Name);- The preceding code defines a class to represent the JSON object returned from the GitHub API. You'll use this class to display a list of repository names. - The JSON for a repository object contains dozens of properties, but only the - Nameproperty will be deserialized. The serializer automatically ignores JSON properties for which there is no match in the target class. This feature makes it easier to create types that work with only a subset of fields in a large JSON packet.- Although the - GetFromJsonAsyncmethod you will use in the next point has a benefit of being case-insensitive when it comes to property names, the C# convention is to capitalize the first letter of property names.
- Use the HttpClientJsonExtensions.GetFromJsonAsync method to fetch and convert JSON into C# objects. Replace the call to GetStringAsync(String) in the - ProcessRepositoriesAsyncmethod with the following lines:- var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos");- The updated code replaces GetStringAsync(String) with HttpClientJsonExtensions.GetFromJsonAsync. - The first argument to - GetFromJsonAsyncmethod is an- awaitexpression.- awaitexpressions can appear almost anywhere in your code, even though up to now, you've only seen them as part of an assignment statement. The next parameter,- requestUriis optional and doesn't have to be provided if was already specified when creating the- clientobject. You didn't provide the- clientobject with the URI to send request to, so you specified the URI now. The last optional parameter, the- CancellationTokenis omitted in the code snippet.- The - GetFromJsonAsyncmethod is generic, which means you supply type arguments for what kind of objects should be created from the fetched JSON text. In this example, you're deserializing to a- List<Repository>, which is another generic object, a System.Collections.Generic.List<T>. The- List<T>class stores a collection of objects. The type argument declares the type of objects stored in the- List<T>. The type argument is your- Repositoryrecord, because the JSON text represents a collection of repository objects.
- Add code to display the name of each repository. Replace the lines that read: - Console.Write(json);- with the following code: - foreach (var repo in repositories ?? Enumerable.Empty<Repository>()) Console.WriteLine(repo.Name);
- The following - usingdirectives should be present at the top of the file:- using System.Net.Http.Headers; using System.Net.Http.Json;
- Run the app. - dotnet run- The output is a list of the names of the repositories that are part of the .NET Foundation. 
Refactor the code
The ProcessRepositoriesAsync method can do the async work and return a collection of the repositories. Change that method to return Task<List<Repository>>, and move the code that writes to the console near its caller.
- Change the signature of - ProcessRepositoriesAsyncto return a task whose result is a list of- Repositoryobjects:- static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
- Return the repositories after processing the JSON response: - var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos"); return repositories ?? new();- The compiler generates the - Task<T>object for the return value because you've marked this method as- async.
- Modify the Program.cs file, replacing the call to - ProcessRepositoriesAsyncwith the following to capture the results and write each repository name to the console.- var repositories = await ProcessRepositoriesAsync(client); foreach (var repo in repositories) Console.WriteLine(repo.Name);
- Run the app. - The output is the same. 
Deserialize more properties
In the following steps, we extend the code to process more properties from the JSON payload returned by the GitHub API. You probably won't need to process every property, but adding a few demonstrates additional C# features.
- Replace the contents of the - Repositoryclass with the following- recorddefinition. Make sure to import the- System.Text.Json.Serializationnamespace and apply the- [JsonPropertyName]attribute to map JSON fields to C# properties explicitly.- using System.Text.Json.Serialization; public record class Repository( string Name, string Description, [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl, Uri Homepage, int Watchers, [property: JsonPropertyName("pushed_at")] DateTime LastPushUtc );- The Uri and - inttypes have built-in functionality to convert to and from string representation. No extra code is needed to deserialize from JSON string format to those target types. If the JSON packet contains data that doesn't convert to a target type, the serialization action throws an exception.- JSON often uses - lowercaseor- snake_casefor property names. Fields like- html_urland- pushed_atdo not follow C# PascalCase naming conventions. Using- [JsonPropertyName]ensures that these JSON keys are correctly bound to their corresponding C# properties, even when their names differ in case or contain underscores. This approach guarantees predictable and stable deserialization while allowing PascalCase property names in C#. Additionally, the- GetFromJsonAsyncmethod is- case-insensitivewhen matching property names, so no further conversion is necessary.
- Update the - foreachloop in the Program.cs file to display the property values:- foreach (var repo in repositories) { Console.WriteLine($"Name: {repo.Name}"); Console.WriteLine($"Homepage: {repo.Homepage}"); Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}"); Console.WriteLine($"Description: {repo.Description}"); Console.WriteLine($"Watchers: {repo.Watchers:#,0}"); Console.WriteLine(); }
- Run the app. - The list now includes the additional properties. 
Add a date property
The date of the last push operation is formatted in this fashion in the JSON response:
2016-02-08T21:27:00Z
This format is for Coordinated Universal Time (UTC), so the result of deserialization is a DateTime value whose Kind property is Utc.
To get a date and time represented in your time zone, you have to write a custom conversion method.
- In Repository.cs, add a property for the UTC representation of the date and time and a readonly - LastPushproperty that returns the date converted to local time, the file should look like the following:- using System.Text.Json.Serialization; public record class Repository( string Name, string Description, [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl, Uri Homepage, int Watchers, [property: JsonPropertyName("pushed_at")] DateTime LastPushUtc ) { public DateTime LastPush => LastPushUtc.ToLocalTime(); }- The - LastPushproperty is defined using an expression-bodied member for the- getaccessor. There's no- setaccessor. Omitting the- setaccessor is one way to define a read-only property in C#. (Yes, you can create write-only properties in C#, but their value is limited.)
- Add another output statement in Program.cs: again: - Console.WriteLine($"Last push: {repo.LastPush}");
- The complete app should resemble the following Program.cs file: - using System.Net.Http.Headers; using System.Net.Http.Json; using HttpClient client = new(); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json")); client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter"); var repositories = await ProcessRepositoriesAsync(client); foreach (var repo in repositories) { Console.WriteLine($"Name: {repo.Name}"); Console.WriteLine($"Homepage: {repo.Homepage}"); Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}"); Console.WriteLine($"Description: {repo.Description}"); Console.WriteLine($"Watchers: {repo.Watchers:#,0}"); Console.WriteLine($"{repo.LastPush}"); Console.WriteLine(); } static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client) { var repositories = await client.GetFromJsonAsync<List<Repository>>("https://api.github.com/orgs/dotnet/repos"); return repositories ?? new List<Repository>(); }
- Run the app. - The output includes the date and time of the last push to each repository. 
Next steps
In this tutorial, you created an app that makes web requests and parses the results. Your version of the app should now match the finished sample.
Learn more about how to configure JSON serialization in How to serialize and deserialize (marshal and unmarshal) JSON in .NET.