Search code examples
c#json.net-coreflurl

JSON deserialization error when property name is missing


I am trying to consume an external web service and I am using .NET Core and the Flurl framework. I get a response from the service like below:

[
   "Successful Request: 96 Results",
   [
      {
         "eventdate":"2019-10-18",
         "name":"",
         "url":"",
         "info":"",
         "showtime":null,
         "url_tix":"",
         "event_owner":"xxx",
         "follow_url":"xxx",
         "event_image":"xxx",
         "venue":"xxx",
         "city":"xxx",
         "country":"xxx",
         "state":""
      }
   ]
]

and I have a C# entity definition like below:

public class ServiceResponce
{
    public Event[] Events { get; set; }
}

public class Event
{
    [JsonProperty("eventdate")]
    public DateTimeOffset Eventdate { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("url")]
    public string Url { get; set; }

    [JsonProperty("info")]
    public string Info { get; set; }

    [JsonProperty("showtime")]
    public object Showtime { get; set; }

    [JsonProperty("url_tix")]
    public object UrlTix { get; set; }

    [JsonProperty("event_owner")]
    public string EventOwner { get; set; }

    [JsonProperty("follow_url")]
    public Uri FollowUrl { get; set; }

    [JsonProperty("event_image")]
    public object EventImage { get; set; }

    [JsonProperty("venue")]
    public string Venue { get; set; }

    [JsonProperty("city")]
    public string City { get; set; }

    [JsonProperty("country")]
    public string Country { get; set; }

    [JsonProperty("state")]
    public string State { get; set; }
}

When I tried to call the Flurl method to consume the web service like below:

var result = await serviceUrl.GetJsonAsync<ServiceResponce>();

I got the error mentioned below:

Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'xxx.ServiceResponce' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path '', line 1, position 1.

Do you have any solution for that? Any help always is welcome.


Solution

  • The problem here is the JSON response is actually an array of mixed types. The first element of the array is a string, and the second element is an array of event objects. You will need a custom JsonConverter to deserialize this JSON.

    Here is the code you would need for the converter:

    class ServiceResponceConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(ServiceResponce));
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JArray ja = JArray.Load(reader);
            ServiceResponce resp = new ServiceResponce();
    
            resp.Events = ja[1].ToObject<Event[]>(serializer);
    
            return resp;
        }
    
        public override bool CanWrite => false;
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    Then, add a [JsonConverter] attribute to the ServiceResponce class to tie it to the converter:

    [JsonConverter(typeof(ServiceResponceConverter))]
    public class ServiceResponce
    {
        public Event[] Events { get; set; }
    }
    

    Now you can deserialize to the ServiceResponce class as normal and it will work properly.

    Optional: If you also want to capture the "Successful Request: 96 Results" string from the response, add

    public string ResultString { get; set; }
    

    to the ServiceResponce class and add the following line to the the ReadJson method of the converter:

    resp.ResultString = (string)ja[0];
    

    Working demo here: https://dotnetfiddle.net/opPUmX