Welcome to this lesson on Binding Complex Types to JSON Bodies. In our previous lesson, we delved into model binding by understanding what it is, examining what HTTP requests consist of, and learning how to bind simple types such as route and query parameters. This lesson will build on that foundation by exploring how to bind complex types, like classes, to JSON bodies.
When working with APIs, data is frequently exchanged in the JSON format. In ASP.NET Core, model binding enables us to map this JSON data to Plain Old CLR Objects (POCOs). This process is known as deserialization. Conversely, converting POCOs into JSON format for HTTP responses is called serialization. Both serialization and deserialization are essential for seamless API communication.
Consider the following code snippet:
C#1public class Todo 2{ 3 public Guid Id { get; set; } 4 public string Description { get; set; } 5 public string Status { get; set; } 6} 7 8var app = WebApplication.CreateBuilder(args).Build(); 9 10app.MapPost("/todo", (Todo todo) => $"Received {todo}"); 11 12app.Run();
In this example, the JSON body received at the /todo
endpoint is deserialized into a Todo
object. This demonstrates how ASP.NET Core automatically maps JSON data to a complex type.
Deserialization in ASP.NET Core is handled by the System.Text.Json
library. This library converts JSON data into POCOs using these key rules:
- Property Name Matching: JSON properties are matched to public properties of the target class. By default, properties are case-sensitive.
- Supported Data Types: Common data types like primitives, arrays, lists, and dictionaries are supported for deserialization.
- Default Constructors: The target class must have a public parameterless constructor, or use a constructor that can match the JSON properties.
Watch out for pitfalls such as:
- Case Sensitivity: JSON property names must match exactly unless configured otherwise.
- Missing Properties: If the JSON contains properties not present in the class, they are ignored.
If JSON fields are not provided in the JSON body, ASP.NET Core does not automatically enforce validation for required fields in minimal APIs. To ensure certain fields have default values when not provided, you can initialize those properties within the class definition. Here's an example:
C#1public class Todo 2{ 3 public Guid Id { get; set; } = Guid.NewGuid(); 4 public string Description { get; set; } = "Default Description"; 5 public string Status { get; set; } = "Not Started"; 6} 7 8var app = WebApplication.CreateBuilder(args).Build(); 9 10app.MapPost("/todo", (Todo todo) => $"Received {todo}"); 11 12app.Run();
In this example, if the incoming JSON does not provide values for Id
, Description
, or Status
, the properties will be initialized with the default values specified.
Be aware that if the JSON body includes values for these fields, those values will override the defaults. This approach guarantees that your objects always start with sensible initial values, even if the client does not provide them.
In this lesson, we learned how to bind complex types to JSON bodies by understanding deserialization, examining the rules and pitfalls involved. In the upcoming practice, you'll apply these concepts by creating and testing endpoints that handle complex types.