Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialization of inteface types not supported using System.Text.JSON

I am passing in a valid JSON object to my controller on my .net core 3 web api application. Doing so, I get the error:

System.NotSupportedException: Deserialization of interface types is not supported. Type 'OrderTranslationContracts.OrderContracts+IImportOrderLineModel'

So I looked in my code and I have the following concrete implementation of an Interface. Here is the line I think that's throwing the error:

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

Here is the portion of the JSON I am passing into the controller:

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

So I know the JSON is valid. Here is the signature for the controller:

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

I don't even break into this code as I am assuming that the JSON deserializer is throwing the error as it tries to convert it. So how do I over come this error? I am bound by the concrete implementation for the interface so I can't change the interface I need to work with what I have here if possible. If that isn't possible, are there any "work arounds" for this?

like image 917
john Avatar asked Dec 05 '19 20:12

john


People also ask

Is polymorphic deserialization possible in System text JSON?

Text. Json doesn't support the serialization of polymorphic type hierarchies. For example, if a property's type is an interface or an abstract class, only the properties defined on the interface or abstract class are serialized, even if the runtime type has additional properties.

How do I deserialize a JSON file?

A common way to deserialize JSON is to first create a class with properties and fields that represent one or more of the JSON properties. Then, to deserialize from a string or a file, call the JsonSerializer. Deserialize method.

What is System Text JSON?

Text. Json. Serialization namespace, which contains attributes and APIs for advanced scenarios and customization specific to serialization and deserialization.

What is deserialization of JSON?

JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).


1 Answers

I had the same problems with HttpClient.GetFromJsonAsync I tried httpClient.GetFromJsonAsync<ICustomer>(url);

And I got the error:

Deserialization of inteface types not supported using System.Text.JSON

From what I was able to find out, a model has to be available to deserialize an InterfaceType. My solution defines the model in the interface using data annotations.

  1. Create a TypeConverter (I found this class here: 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 Add Data Annotations in your Interface ([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 Unfortunatly HttpClient.GetFromJsonAsync does not use Newtonsoft. I wrote my own method

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 Now i can use:

 HttpClient httpClient= new HttpClient();
 string url="https://example.com/api/customerlist";
 var myCustomerList[]=await GetJsonAsync<CutomerModel[]>(httpClient, url);
like image 142
Chris Berlin Avatar answered Oct 13 '22 05:10

Chris Berlin