Yoana Gancheva
Yoana Gancheva

Reputation: 619

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 'Sync.Api.Controllers.AddDocumentRequestModel'

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

Upvotes: 44

Views: 89302

Answers (10)

pkucas
pkucas

Reputation: 187

I encountered this error when passing a DTO to my Api controller, which was receiving it as a parameter using [FromBody]. In my case I was inheriting from an abstract class. This class had an empty constructor and so did my child class. I tried fixing it with the [JsonConstructor] annotation, and I even tried a custom JsonConverter. Neither of these worked.

My problem was my abstract class did not have { get; set; } on its public properties. I created this class with a parameterized constructor to avoid it being an anemic domain model, but did not think (or maybe just forgot) it would need the getters and setters. Be sure the JSON property names (if you have the Newtonsoft [JsonProperty] or System.Text.Json.Serialization [JsonPropertyName] attributes) also match. I spent a while on this one!

Upvotes: 0

HydTechie
HydTechie

Reputation: 807

Microsoft proves its weirdness again and again.. Though error bears out json attribute or parameterless.. I did a copy paste of different route and changed it sensibly with the "Same" model, its started working fine...

Upvotes: 0

Ali
Ali

Reputation: 359

Just add [JsonConstructor] before your constructor like this:

public class Person
{
    public string Name { get; set; }
    public int LuckyNumber { get; private set; }
    
    [JsonConstructor]
    public Person(int luckyNumber)
    {
        LuckyNumber = luckyNumber;
    }
    public Person() { }
}

Upvotes: 10

Ogglas
Ogglas

Reputation: 69968

I got the following runtime error using Blazor WASM (Blazor WebAssembly) and System.Text.Json:

fail: MyProject.Client.Shared.Error[0] Error:ProcessError - Type: System.NotSupportedException Message: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'MyProject.Shared.Models.DTO.MyDto'. Path: $[0].myDtos[0] | LineNumber: 0 | BytePositionInLine: 406. Exception: System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'MyProject.Shared.Models.DTO.MyDto'. Path: $[0].myDtos[0] | LineNumber: 0 | BytePositionInLine: 406. ---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'MyProject.Shared.Models.DTO.MyDto'.

Using you model the easiest fix in my world is simply adding a parameterless constructor:

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

    public AddDocumentRequestModel()
    {
    }

    [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; }
}

If you need default values use constructor chaining which also works:

public AddDocumentRequestModel() : this(1, new List<ProductRequestModel>() { new ProductRequestModel()})
{
}

https://stackoverflow.com/a/1814965/3850405

Upvotes: 0

Alex SSantos
Alex SSantos

Reputation: 43

Just removed the constructor of my class. Resolved for me.

Upvotes: 0

Rombersoft
Rombersoft

Reputation: 1

In my case error, caused was inside InnerException. There is my class had a field with a custom class type that did not have a parameterless constructor. I've added a parameterless constructor to the inner class and the problem has gone away.

Upvotes: 0

Mark Schultheiss
Mark Schultheiss

Reputation: 34168

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; }
}

Upvotes: 1

IBRA
IBRA

Reputation: 1782

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.

Upvotes: 41

Adrian Nasui
Adrian Nasui

Reputation: 1095

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;

Upvotes: 43

jawa
jawa

Reputation: 336

There are still some limitations using System.Text.Json - have a look here: https://learn.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://learn.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

Upvotes: 1

Related Questions