不支持反序列化没有无参构造函数的引用类型。

37

我有这个API

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

            return BadRequest();
        }

这是我的请求模型

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

所以当我尝试使用这个请求体命中API时

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

这是请求:

System.NotSupportedException: 不支持反序列化没有无参构造函数的引用类型。类型 'Alati.Commerce.Sync.Api.Controllers.AddDocumentRequestModel'

我不需要一个无参构造函数,因为它不读取主体参数。有没有其他方法进行反序列化?


2
一些IDE(例如Rider)会建议您将用于序列化的类转换为抽象类。删除abstract关键字(改用具体类),可以解决比OP提到的许多其他麻烦的异常。 - tortal
1
应该可以在.NET 5.0中正常工作。请参阅 https://github.com/dotnet/runtime/issues/41313 - Palec
在.NET 6中,当处理字典键中的System.Uri时,我们仍然需要使用Newtonsoft.Json。 https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-supported-collection-types?pivots=dotnet-6-0#supported-key-types - Palec
@tortal 你的回复解决了我的问题。 - Abayomi Olowu
8个回答

41
你可以实现所需的结果。你需要切换到NewtonsoftJson序列化(从Microsoft.AspNetCore.Mvc.NewtonsoftJson包中)。在Startup.cs的ConfigureServices方法中调用此方法:
services.AddControllers().AddNewtonsoftJson();

在此之后,您的构造函数将被反序列化调用。
额外信息:我正在使用ASP Net Core 3.1
后续编辑:我想提供更多关于此的信息,因为似乎也可以通过使用System.Text.Json实现,尽管需要自定义实现。来自jawa的答案指出,可以通过创建自定义转换器(继承自JsonConverter)并将其注册到转换器集合(JsonSerializerOptions.Converters)中来实现反序列化为不可变类和结构
public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
{
...
}

然后...

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

2
请确保您正在安装适用于您使用的 .Net 或 .Net Core 框架的正确版本的 Microsoft.AspNetCore.Mvc.NewtonsoftJson。尝试安装 5.x 版本的 .Net Core 3.1 不起作用,但安装 3.1.x 版本可以。{facepalm} - computercarguy
1
在.NET 6中,当处理字典键中的System.Uri时,我们仍然需要采用这种解决方案。https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-supported-collection-types?pivots=dotnet-6-0#supported-key-types - Palec

21

如果有人遇到和我同样的问题,我是在使用abstract类时,一旦去掉了abstract关键字,就一切正常了。


1
任何被抽象化的函数都可以声明为虚拟函数(virtual)。同时,在这些抽象函数中加入**throw new NotImplementedException();**,以便捕获未重写它们的派生类。 - Pic Mickael

5

在你的构造函数前加上 [JsonConstructor]

就像这样:

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

无法解决问题 - Greg

1
在我的情况下,我将一个类设为了internal,当我将其改为public时它就能工作了。对于这种特定情况,错误信息真的没有什么帮助。
旧代码(实际类名在示例中更改为ClassName
internal class Rootobject
{
    [JsonConstructor]
    public Rootobject(ClassName className)
    {
        ClassName = className?? throw new ArgumentNullException(nameof(className));
    }

    public ClassName ClassName { get; set; }
}

更新:

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

    public ClassName ClassName { get; set; }
}

1

使用System.Text.Json仍存在一些限制-请参阅此处:https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson 目前不支持使用带参数构造函数进行反序列化(但已计划)。您可以实现自定义JsonConverter(例如在此示例中:https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#deserialize-to-immutable-classes-and-structs),或者像Adrian Nasul建议的那样:使用Newtonsoft.Json,然后您可以使用[JsonConstructor]属性


这是他们在 .Net 5 中引入的一种有意限制。我认为它永远不会被修复,从本质上讲,使 System.Text.Json 永远成为 it just works Newtonsoft.Json 的 just doesn't work 伙伴。https://learn.microsoft.com/en-us/dotnet/core/compatibility/serialization/5.0/non-public-parameterless-constructors-not-used-for-deserialization - Douglas Gaskell

0
在我的错误案例中,造成问题的是InnerException。我的类有一个使用自定义类类型的字段,但该字段没有无参构造函数。我为内部类添加了一个无参构造函数,这个问题就解决了。

0

刚刚移除了我的类的构造函数。对我来说解决了问题。


0

我在使用 Blazor WASM(Blazor WebAssembly)和 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'。

根据你的模型,在我的世界里最简单的解决方法就是添加一个无参构造函数:

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

如果您需要默认值,请使用构造函数链接,这也是有效的:

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

https://dev59.com/13I-5IYBdhLWcg3whYor#1814965


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接