'Deserialization of reference types without parameterless constructor is not supported

I have this API

 public ActionResult AddDocument([FromBody]AddDocumentRequestModel documentRequestModel)
        {
            AddDocumentStatus documentState = _documentService.AddDocument(documentRequestModel, DocumentType.OutgoingPosShipment);
            if (documentState.IsSuccess)
                return Ok();

            return BadRequest();
        }

And this is my request model

    public class AddDocumentRequestModel
    {
        public AddDocumentRequestModel(int partnerId, List<ProductRequestModel> products)
        {
            PartnerId = partnerId;
            Products = products;
        }

        [Range(1, int.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
        public int PartnerId { get; private set; }

        [Required, MustHaveOneElement(ErrorMessage = "At least one product is required")]
        public List<ProductRequestModel> Products { get; private set; }
    }

so when I'm trying to hit the API with this body

{
        "partnerId": 101,
        "products": [{
            "productId": 100,
            "unitOfMeasureId": 102,
            "quantity":5
        }
     ]
}

this is the request : System.NotSupportedException: Deserialization of reference types without parameterless constructor is not supported. Type 'Alati.Commerce.Sync.Api.Controllers.AddDocumentRequestModel'

I don't need parameterless constructor,because it doesn't read the body parameters.Is there any other way for deserialization?



Solution 1:[1]

You can achieve your desired result. You need to switch to NewtonsoftJson serialization (from package Microsoft.AspNetCore.Mvc.NewtonsoftJson)

Call this in Startup.cs in the ConfigureServices method:

services.AddControllers().AddNewtonsoftJson();

After this, your constructor will be called by deserialization.

Extra info: I am using ASP Net Core 3.1

Later Edit: I wanted to give more info on this, as it seems that this can also be achieved by using System.Text.Json, although custom implementation is necessary. The answer from jawa states that Deserializing to immutable classes and structs can be achieved with System.Text.Json, by creating a custom converter (inherit from JsonConverter) and registering it to the converters collection (JsonSerializerOptions.Converters) like so:

public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
{
...
}

and then...

var serializeOptions = new JsonSerializerOptions();
serializeOptions.Converters.Add(new ImmutablePointConverter());
serializeOptions.WriteIndented = true;

Solution 2:[2]

Just in case someone have the same issue I had, I was using abstract class, once removed the abstract key word, it all worked just fine.

Solution 3:[3]

There are still some limitations using System.Text.Json - have a look here: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson Deserialization without parameterless constructor using a parameterized constructor is not supported yet (but it's on their plan). You can implement your custom JsonConverter (like in this example: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#deserialize-to-immutable-classes-and-structs) or - like Adrian Nasul above suggested: use Newtonsoft.Json and then you can use the [JsonConstructor] attribute

Solution 4:[4]

In my case I had set a class as internal and when I made it public it worked. The error message was really of little help with this specific circumstance.

Old (actual class name changed to ClassName in the example

internal class Rootobject
{
    [JsonConstructor]
    public Rootobject(ClassName className)
    {
        ClassName = className?? throw new ArgumentNullException(nameof(className));
    }

    public ClassName ClassName { get; set; }
}

New:

public class Rootobject
{
    [JsonConstructor]
    public Rootobject(ClassName className)
    {
        ClassName = branding ?? throw new ArgumentNullException(nameof(className));
    }

    public ClassName ClassName { get; set; }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pang
Solution 2 IBRA
Solution 3 jawa
Solution 4 Mark Schultheiss