Kyle Rain

Parameterless constructor for System.Text.Json

Today I needed to deserialize an HTTP response to my custom type ApiResponse<T> in an integration test:

ApiResponse<ItemDto>? itemResponse = await response.Content.ReadFromJsonAsync<ApiResponse<ItemDto>>();

This resulted in the error System.NotSupportedException : Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported.

This was my custom type ApiResponse<T>:

public class ApiResponse<T>
{
    public T? Data { get; set; }
    public string Message { get; set; }
    public ApiResponseType Type { get; set; }

    public ApiResponse(T data, string msg, ApiResponseType type)
    {
        Data = data;
        Message = msg;
        Type = type;
    }

    public ApiResponse(string msg, ApiResponseType type)
    {
        Data = default;
        Message = msg;
        Type = type;
    }
}

I added this parameterless constructor, which solved the issue:

public ApiResponse()
{
    Message = "";
}

ApiResponseType is an enum (a value type) so it gets a default value of 0.

The relevant documentation on this issue seems to be Use immutable types and properties which explains:

By default, System.Text.Json uses the default public parameterless constructor.

In C#, classes have a default parameterless constructor only if they do not have any other constructors defined.

However, you can tell it to use a parameterized constructor, which makes it possible to deserialize an immutable class or struct.

For a class, if the only constructor is a parameterized one, that constructor will be used.

For a struct, or a class with multiple constructors, specify the one to use by applying the [JsonConstructor] attribute. When the attribute is not used, a public parameterless constructor is always used if present.

So in my case of a class with constructor overloads and none with the JsonConstructor attribute, System.Text.Json is unable to determine a constructor to use leading to the exception, and adding a parameterless constructor to the class provides a solution.

This project is maintained by KyleRego