0

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.

1
  • I had only get; in the property, because of that also not serialized :( . After adding Set; also into the property def, serialized correctly. Hope It Helps! Commented Oct 31, 2021 at 19:52

2 Answers 2

2

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

Sign up to request clarification or add additional context in comments.

Comments

0

I think the problem is in the Json object, I have generated a class with 'Newtonsoft.Json', if you can try this code:

// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
//    using MyNameSpace;
//
//    var event = Event.FromJson(jsonString);

namespace MyNameSpace
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;

    public partial class EventClass
    {
        [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 string UrlTix { get; set; }

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

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

        [JsonProperty("event_image")]
        public string 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; }
    }

    public partial struct EventUnion
    {
        public EventClass[] EventClassArray;
        public string String;

        public static implicit operator EventUnion(EventClass[] EventClassArray) => new EventUnion { EventClassArray = EventClassArray };
        public static implicit operator EventUnion(string String) => new EventUnion { String = String };
    }

    public class Event
    {
        public static EventUnion[] FromJson(string json) => JsonConvert.DeserializeObject<EventUnion[]>(json, MyNameSpace.Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this EventUnion[] self) => JsonConvert.SerializeObject(self, MyNameSpace.Converter.Settings);
    }

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters =
            {
                EventUnionConverter.Singleton,
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }

    internal class EventUnionConverter : JsonConverter
    {
        public override bool CanConvert(Type t) => t == typeof(EventUnion) || t == typeof(EventUnion?);

        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
        {
            switch (reader.TokenType)
            {
                case JsonToken.String:
                case JsonToken.Date:
                    var stringValue = serializer.Deserialize<string>(reader);
                    return new EventUnion { String = stringValue };
                case JsonToken.StartArray:
                    var arrayValue = serializer.Deserialize<EventClass[]>(reader);
                    return new EventUnion { EventClassArray = arrayValue };
            }
            throw new Exception("Cannot unmarshal type EventUnion");
        }

        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
        {
            var value = (EventUnion)untypedValue;
            if (value.String != null)
            {
                serializer.Serialize(writer, value.String);
                return;
            }
            if (value.EventClassArray != null)
            {
                serializer.Serialize(writer, value.EventClassArray);
                return;
            }
            throw new Exception("Cannot marshal type EventUnion");
        }

        public static readonly EventUnionConverter Singleton = new EventUnionConverter();
    }
}

2 Comments

Thanks for your answer. Actually I tried the same thing by using an online json to c# object tool. But I got another error. Like below: Error converting value "Successful Request: 96 Results" to type 'DataMigration.EventClass'. Path '[0]', line 1, position 33.
I think I got this error because "Successful Request: 96 Results" does not have property name but only value.The thing is I cannot touch the service result since it is external.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.