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.