Understanding How Web API Interfaces Return Collections of Implementation Classes

Understanding How Web API Interfaces Return Collections of Implementation Classes

The .NET Web API returns a list of base class objects. When testing, the endpoint only returns properties of the base class. How can the properties of the implementation class be returned?

Last updated 3/19/2023 8:55 PM
沙漠尽头的狼
4 min read
Category
ASP.NET Core
Tags
.NET C# ASP.NET Core Web API

Hello everyone, I am the Wolf at the End of the Desert.

1. Problem Description

As shown below, define two subclasses Student and Employ, both inheriting from the abstract class PersonBase:

public abstract class PersonBase
{
    public string Name { get; set; }

    protected PersonBase(string name)
    {
        Name = name;
    }
}

public class Student : PersonBase
{
    public string Number { get; set; }

    public Student(string name, string number) : base(name)
    {
        Number = number;
    }
}

public class Employ : PersonBase
{
    public string CompanyName { get; set; }

    public Employ(string name, string companyName) : base(name)
    {
        CompanyName = companyName;
    }
}

Add a Web API endpoint that returns a collection of the base class:

[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    [HttpGet(Name = "GetDetails")]
    public IEnumerable<PersonBase> Get()
    {
        return new List<PersonBase>()
        {
            new Student("Student A", "Student No. 01"),
            new Employ("Employee 01", "Baidu")
        };
    }
}

API response:

[
  {
    "name": "Student A"
  },
  {
    "name": "Employee 01"
  }
]

Notice the problem? The extended properties of the Student and Employ instances (Number of Student, CompanyName of Employ) are not serialized. How can we serialize all properties of the derived classes?

2. Implementing Serialization of All Properties of the Derived Classes

Referencing the Microsoft documentation "How to serialize properties of derived classes with System.Text.Json", there are two relatively simple approaches.

2.1. Approach for .NET 7 and earlier

Before .NET 7, System.Text.Json did not support serialization of polymorphic type hierarchies. For example, if the return type of an endpoint is an interface or an abstract class collection, only the properties defined by the interface or abstract class are serialized, even if the runtime type has additional properties.

Solution: Change the endpoint return type from IEnumerable<PersonBase> to object, and change the List<PersonBase> implementation to List<object>:

[HttpGet(Name = "GetDetails")]
public object Get()
{
    return new List<object>()
    {
        new Student("Student A", "Student No. 01"),
        new Employ("Employee 01", "Baidu")
    };
}

After the change, the endpoint successfully returns detailed JSON information:

[
  {
    "number": "Student No. 01",
    "name": "Student A"
  },
  {
    "companyName": "Baidu",
    "name": "Employee 01"
  }
]

Principle: After changing to object, serialization defaults to serializing the concrete implementation classes. Before the change, System.Text.Json only recognized the parent class.

2.2. Approach for .NET 7 and later

Starting from .NET 7, System.Text.Json supports serialization and deserialization of polymorphic type hierarchies using attribute annotations.

We restore the endpoint to its original signature and add attributes to the abstract class to specify which derived types should be mapped during base class serialization:

[JsonDerivedType(typeof(Student))]
[JsonDerivedType(typeof(Employ))]
public abstract class PersonBase

Problem solved, the endpoint returns the same JSON as above.

The documentation describes JsonDerivedTypeAttribute: When placed on a type declaration, it indicates that the specified derived type should be selected for polymorphic serialization. It also exposes functionality for specifying type discriminators.

3. Summary

Choose the approach based on the .NET version. The second approach requires you to know the derived types explicitly. For detailed usage, see the Microsoft documentation: How to serialize properties of derived classes with System.Text.Json

If you have a better approach, feel free to leave a comment.

  • WeChat Technical Exchange Group: Add WeChat (codewf) with note "Join Group"
  • QQ Technical Exchange Group: 771992300.

Keep Exploring

Related Reading

More Articles
Same category / Same tag 6/22/2022

ASP.NET Core WebAPI Localization (Single Resource File)

Microsoft's default approach is one class corresponding to multiple resource files, which is cumbersome to use. This article introduces the use of a single resource file, where all classes in the entire project correspond to one set of multilingual resource files.

Continue Reading
Same category / Same tag 4/13/2022

Practice of uniform wrapping of ASP.NET Core WebApi return results

Regarding the unified return of WebApi results, it also made me think further. First, how to better restrict the unified format of the return, and secondly, the packaging of results must be simpler and more powerful. Through continuous thinking and improvement, I finally achieved preliminary results. I share them out. Learning has no end, thinking has no end. I hope this can encourage us together.

Continue Reading