使用System.Text.JSON不支持接口类型的初始化

nukf8bse  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(128)

我正在向我的.net core 3 web API应用程序上的控制器传递一个有效的JSON对象。这样做时,我得到错误:
System.NotSupportedException:不支持接口类型的初始化。类型“OrderTranslationContracts.OrderContracts+IImportOrderLineModel”
所以我看了我的代码,我有下面的接口的具体实现。这里是我认为抛出错误的行:

public List<OrderContracts.IImportOrderLineModel> Lines { get; set; }

字符串
下面是我传递到控制器的JSON部分:

"lines": [
        {
            "orderNumber": "LV21131327",
            "lineNumber": 1,
            "itemId": "3083US",
            "customerItemId": "3083US",
            "quantity": 3,
            "price": 0.00,
            "quantityBackOrdered": null,
            "comments": "",
            "pickLocation": "",
            "orderFilled": "O",
            "hostUom": null,
            "type": null
        }


所以我知道JSON是有效的。下面是控制器的签名:

[HttpPost]
    public async Task<List<ImportOrderModel>> Post([FromBody] List<ImportOrderModel> orders)
    {
        var response = await _validateOrder.ValidateAllOrdersAsync(orders, null);
        return response;
    }


我甚至没有打断这段代码,因为我假设JSON转换器在试图转换它时抛出了错误。那么我如何克服这个错误呢?我受接口的具体实现的约束,所以我不能改变接口,如果可能的话,我需要使用这里的接口。如果这是不可能的,有没有什么“变通办法”?

pb3skfrl

pb3skfrl1#

我遇到了与HttpClient.GetFromJsonAsync相同的问题,我尝试了httpClient.GetFromJsonAsync<ICustomer>(url);
我得到了错误:
使用System.Text.JSON不支持接口类型的初始化
从我所能发现的来看,一个模型必须可以用来对一个接口类型进行描述。我的解决方案是使用数据注解在接口中定义模型。
1.创建一个TypeConverter(我在这里找到了这个类:Casting interfaces for deserialization in JSON.NET

using Newtonsoft.json;

public class ConcreteTypeConverter<TConcrete> : JsonConverter
 {
     public override bool CanConvert(Type objectType)
     {
         //assume we can convert to anything for now
         return true;
     }

     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
     {
         //explicitly specify the concrete type we want to create
         return serializer.Deserialize<TConcrete>(reader);
     }

     public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
     {
         //use the default serialization - it works fine
         serializer.Serialize(writer, value);
     }
 }

字符串
2在接口中添加数据注解([JsonConverter(typeof(ConcreteTypeConverter<AddressModel>))]

using Newtonsoft.Json;
public interface ICustomer
{
    int Id { get; set; }
    int Name { get; set; }

    [JsonConverter(typeof(ConcreteTypeConverter<AddressModel>))]
    IAddress Address { get; set; }
}


3不幸的是HttpClient.GetFromJsonAsync没有使用Newtonsoft。我自己写了方法

public async static Task<T> GetJsonAsync<T>(HttpClient client, string url)
{
    using var response = await client.GetAsync(url);
    response.EnsureSuccessStatusCode();

    using Stream stream = await response.Content.ReadAsStreamAsync();
    using (var reader = new StreamReader(stream, Encoding.UTF8))
    {
        return JsonConvert.DeserializeObject<T>(reader.ReadToEnd(), new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Objects,
            NullValueHandling= NullValueHandling.Ignore
        });
    }
}


4现在我可以用途:

HttpClient httpClient= new HttpClient();
 string url="https://example.com/api/customerlist";
 var myCustomerList[]=await GetJsonAsync<CutomerModel[]>(httpClient, url);

ui7jx7zq

ui7jx7zq2#

我也是在收到一个异常后看到这篇文章的,但是发现使用属性有点适得其反。使用接口的全部原因是这些接口对实现一无所知。通过指定属性,接口变得与实现强耦合,这违背了使用它的目的。
这就是为什么我想出了另一个解决方案,它适用于我,而不需要用属性强烈引用它。

型号

public class Budget : ModelBase, IBudget
{
    /// <summary>
    /// Gets or sets the BudgetId property value.
    /// </summary>
    [Required]
    [Identity]
    public Guid BudgetId
    {
        get => GetValue<Guid>();
        set => SetValue(value);
    }

    /// <summary>
    /// Gets or sets the Description property value.
    /// </summary>
    [Required]
    [StringLength(255)]
    public string Description
    {
        get => GetValue<string>();
        set => SetValue(value);
    }

    ...
}

字符串

接口

public interface IBudget : IModelBase
{
    /// <summary>
    /// Gets or sets the BudgetId property value.
    /// </summary>
    [Required]
    [Identity]
    Guid BudgetId { get; set; }

    /// <summary>
    /// Gets or sets the Description property value.
    /// </summary>
    [Required]
    [StringLength(255)]
    string Description { get; set; }
   ...
}

TypeResolver

public static class TypeResolvers
{
    public static Action<JsonTypeInfo> GetBudgetsTypeResolvers()
    {
        return typeInfo =>
        {
            if (typeInfo.Type == typeof(IBudget))
                typeInfo.CreateObject = () => new Budget();
        };
    }
}

Dependent注入(例如在hostbuilder内部)

services.AddSingleton<JsonSerializerOptions>((s) =>
{
    var options = new JsonSerializerOptions
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        PropertyNameCaseInsensitive = true,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        ReadCommentHandling = JsonCommentHandling.Skip,
        AllowTrailingCommas = true,
        NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString,
        ReferenceHandler = null,
        TypeInfoResolver = new DefaultJsonTypeInfoResolver
        {
            Modifiers = { TypeResolvers.GetBudgetsTypeResolvers() }
        }
    };

    options.Converters.Add(new JsonStringEnumConverter());
    options.Converters.Add(new IsoDateTimeJsonConverter());
    options.Converters.Add(new IsoDateTimeOffsetJsonConverter());

    return options;
});
services.TryAddSingleton<IRestService, RestService>();

RestService

public partial class RestService : BaseRestService, IRestService
{
    /// <summary>
    /// Default constructor.
    /// </summary>
    /// <param name="context"></param>
    /// <param name="authenticationService"></param>
    /// <param name="languageService"></param>
    /// <param name="exceptionHandlerService"></param>
    /// <param name="configurationOptions"></param>
    public RestService(
        IContext context,
        IAuthenticationService authenticationService,
        ILanguageService languageService,
        IExceptionHandlerService exceptionHandlerService,
        JsonSerializerOptions jsonSerializerOptions,
        IOptions<ConfigurationOptions> configurationOptions)
        : base(context, authenticationService, languageService, exceptionHandlerService, jsonSerializerOptions, configurationOptions)
    {
    }

    /// <summary>
    /// Gets the budget by identifier asynchronous.
    /// </summary>
    /// <param name="id">The identifier.</param>
    /// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>Task&lt;Budget&gt;.</returns>
    public Task<IBudget> GetBudgetByIdAsync(Guid id, CancellationToken cancellationToken = default) =>
        GetAsync<IBudget>($"{_configurationOptions.ServiceEndpoint}/{ControllerNames.Budgets}/{id}", cancellationToken: cancellationToken);

    /// <summary>
    /// Adds the budget asynchronous.
    /// </summary>
    /// <param name="e">The e.</param>
    /// <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
    /// <returns>Task&lt;System.Int32&gt;.</returns>
    public Task<int> AddBudgetAsync(IBudget e, CancellationToken cancellationToken = default) =>
        PostAsync<int>($"{_configurationOptions.ServiceEndpoint}/{ControllerNames.Budgets}", e, cancellationToken: cancellationToken);

   ...
}

yuvru6vn

yuvru6vn3#

根据@Chris柏林给出的答案,我尝试了同样的方法,但在我的情况下,这个问题是在控制器级别-这意味着当我从 Postman 那里调用我的API时,我面临着这个问题(操作方法参数模型类有接口属性)。
我修改了我的startup.cs以使用Newtonsoft.json而不是System.json,方法是在我的start.cs中进行以下更改(沿着@Chris柏林的解决方案):

services.AddControllers().AddNewtonsoftJson();
services.AddControllersWithViews().AddNewtonsoftJson();   //optional
services.AddRazorPages().AddNewtonsoftJson();   //optional

字符串
和它的工作.

koaltpgm

koaltpgm4#

在这里:

public List<OrderContracts.IImportOrderLineModel> Lines { get; set; }

字符串
您的列表属于IImportOrderLineModel接口类型。
应该像

public List<ImportOrderLineModel> Lines { get; set; }


ImportOrderLineModel是一个实现它的类,如下所示:

public class ImportOrderLineModel : IImportOrderLineModel
{
    //......
}

相关问题