0

I try to consume an json APIwith Flurl from a website and I'm getting parse errors or conversion errors. I'm not sure what is my problem since is the first time I deal with json files. I utilize the following online tool to create a C# object out of the json file. But when I try to get a response with Flurl I get the following errors.

System.AggregateException: One or more errors occurred. ---> Flurl.Http.FlurlParsingException: Response could not be deserialized to JSON: GET ---> Newtonsoft.Json.JsonSerializationException: Error converting value "{"Group":[{"name":"Senior","id":1},{"name":"Age","id":1}],"Area":[{"AreaID":58,"Description":"Years 2018-19","Week":1150,"taken":true,"LDate":null","Label":"RED"}]

Path '', line 1, position 4814377. ---> System.ArgumentException: Could not cast or convert from System.String to System.Collections.Generic.List`1[JsonTest.SeatTest].

This is the chunk of code that fails:

var response = await url.WithHeaders(new
    User_Agent = ConstantData.UserAgent,
    Accept = Accept,
    Referer = Referer})
    ..GetJsonAsync<List<SeatTest>>()
    .Result;

But now if I use the following:

var response = await url.WithHeaders(new
    User_Agent = ConstantData.UserAgent,
    Accept = Accept,
    Referer = Referer})
    .GetStringAsync()
    .Result;

I get (which I think is normal, not sure) the following string when I do a quick watch or hover over the response variable.

"\"{\\"Group\\":[{\\"name\\":\\"Senior\\",\\"id\\":1},{\\"name\\":\\"Age\\",\\"id\\":1}],\\"Area\\":[{\\"AreaID\\":58,\\"Description\\":\\"Years 2018-19\\",\\"Week\\":1150,\\"taken\\":true,\\"LDate\\":null",\\"Label\\":\\"RED\\"}]

If I apply the following:

var json = JsonConvert.DeserializeObject<SeatTest>(response);

it will also throw the exception that I already post.

OR

If I skip the above line and do the following:

var jSerial = new JavaScriptSerializer();
jSerial.MaxJsonLength = 50000000;
string jsonStr = jSerial.Deserialize<string>(response); 
var data = jSerial.Deserialize<SeatTest>(jsonStr);

My class objects appear null. But I don't get any errors or exceptions.

The that Flurl code that I'm using looks like follow, or at least the one I want to utilize:

 var response = url.WithHeaders(new
                {
                    User_Agent = ConstantData.UserAgent,
                    Accept = Accept,
                    Referer = Referer
                })
                    .GetJsonAsync<List<SeatTest>>()
                    .Result;

And my object model looks like:

public class SeatTest
    {
        [JsonProperty(PropertyName ="Group")]
        public List<Group> groups { get; set; }
        [JsonProperty(PropertyName = "Area")]
        public List<Group> areas { get; set; }
     }

 [Serializable]
    public class Group
    {
        public string name { get; set; }
        public int id { get; set; }

    }

 [Serializable]
 public class Area
    {
        public int AreaID{ get; set; }
        public string Description { get; set; }
        public int Week { get; set; }
        public bool Taken { get; set; }
        public DateTime LDate { get; set; }
        public string Label { get; set; }
    }

I expect an object that is filled with the data that comes from the API, but instead I get exceptions:

System.AggregateException: One or more errors occurred. ---> Flurl.Http.FlurlParsingException: Response could not be deserialized to JSON: GET ---> Newtonsoft.Json.JsonSerializationException: Error converting value "{"Group":[{"name":"Senior","id":1},{"name":"Age","id":1}],"Area":[{"AreaID":58,"Description":"Years 2018-19","Week":1150,"taken":true,"LDate":null","Label":"RED"}]

Path '', line 1, position 4814377. ---> System.ArgumentException: Could not cast or convert from System.String to System.Collections.Generic.List`1[JsonTest.SeatTest].

EDIT

var jSerial = new JavaScriptSerializer();
jSerial.MaxJsonLength = 50000000;
string jsonStr = jSerial.Deserialize<string>(response); 
/*First pass: Here I already got the info that I need, how I map it to my class?, since I would like to use Flurl GetAsync() and map it to my object and not a string */
/*Above line response val:  "\{\\\"Group\\\":[{\\\"name\\\":\\\"Senior\\\",\\\"id\\\":1},{\\\"name\\\":\\\"Age\\\",\\\"id\\\":2}],\\\"Area\\\":[{\\\"AreaID\\\":58,\\\"Description\\\":\\\"Season 2018-2019\\\",\\\"Week\\\":1150,\\\"taken\\\":true,\\\"LDate\\\":\\\"2019-07-07T00:00:00\\\",\\\"Label\\\":\\\"RED\\\"}]} */
/*Above line jsonStr val: null*/

var data = jSerial.Deserialize<SeatTest>(jsonStr);
/*Above line jsonStr val same as raw in postman:  "\{\"Group\":[{\"name\":\"Senior\",\"id\":1},{\"name\":\"Age\",\"id\":2}],\"Area\":[{\"AreaID\":58,\"Description\":\"Season 2018-2019\",\"Week\":1150,\"taken\":true,\"LDate\":\"2019-07-07T00:00:00\",\"Label\":\"RED\"}]} */
/* data value after above line: Group: null, Area: null*/

Raw Postman Json

{
   \"Group\": [
        {
            \"name\": \"Senior\",
            \"id\": 1
        },
        {
            \"name\": \"Adult\",
            \"id\": 2
        }
    ],
    \"Area\": [
        {
            \"AreaID\": 58,
            \"Description\": \"Area 2018-2019\",            
            \"Week\": 1150,
            \"taken\": true,
            \"LDate\": \"2019-07-07T00:00:00\",
            \"Label\": \"RED\"
        },
        {
            \"SeasonID\": 57,
            \"Description\": \"Area 51\",           
            \"Week\": 1,
            \"taken\": true,
            \"LDate\": \"2019-07-015T00:00:00\",
            \"Label\": \"GREEN\"
        }]
}

Json also how JsonStr (first pass) looks like in Quick watch

{
   "Group": [
        {
            "name": "Senior",
            "id": 1
        },
        {
            "name": "Adult",
            "id": 2
        }
    ],
    "Area": [
        {
            "AreaID": 58,
            "Description": "Area 2018-2019",            
            "Week": 1150,
            "taken": true,
            "LDate": "2019-07-07T00:00:00",
            "Label": "RED"
        },
        {
            "SeasonID": 57,
            "Description": "Area 51",           
            "Week": 1,
            "taken": true,
            "LDate": "2019-07-015T00:00:00",
            "Label": "GREEN"
        }]
}
CodeAndvil
  • 15
  • 1
  • 7
  • 1
    Could you post the "clean" JSON of the response? This should be as easy as hitting the endpoint in a browser or Postman and copying the response body. Aside from being easier to look at, it would help ensure the extra quotes/escape characters are being added by the debugger and are not in the actual response. – Todd Menier Jul 26 '19 at 18:00
  • The JSON in your question looks to have been some escaping added by Visual Studio, but if not, it was double-serialized on the server side along the lines of [JSON.NET Parser *seems* to be double serializing my objects](https://stackoverflow.com/q/25559179/3744182). Can you please [edit] your question and confirm what the RAW response JSON looks like, without any escaping? If it was double-serialized the best solution is to fix that on the sever side. – dbc Jul 26 '19 at 18:32
  • The response string that I have post should have \\\ instead of \\. I try to edit it and for some reason its the same. If write it to a file it will appear with only one \, postman also show the string with one \. The response is 354,505 lines long if I beautify the returned Json. The example json that I post is short because Is just a subset of the entire thing. – CodeAndvil Jul 26 '19 at 18:37
  • For null date values I have declare my LDate property as Datetime? in my class and assign today DateTime as default value, also I change it to string, but both modification didn't contribute to a solution. – CodeAndvil Jul 26 '19 at 21:24
  • Thanks for the link @dbc, but sadly I don't have access to the server. – CodeAndvil Jul 29 '19 at 15:25

1 Answers1

1

Let's look at the error message:

Path '', line 1, position 4814377. ---> System.ArgumentException: Could not cast or convert from System.String to System.Collections.Generic.List`1[JsonTest.SeatTest].

This is telling you that somewhere in the JSON response (at the 4,814,377th character to be exact), there's a string where in array is expected. It appears that you're expecting both "Group" and "Area" properties to be arrays in the JSON, but somewhere deep down in the response at least one of them is a string.

One way to work around this is to parse the response string to a JArray and build your strongly-typed list one by one, handling parsing errors along the way:

var results = new List<SeatTest>();

var arr = JArray.Parse(response);
foreach (var obj in arr) {
    try {
        var seat = obj.ToObject<SeatTest>();
        results.Add(seat);
    }
    catch (Exception ex) {
        // parsing error, inspect ex and obj.ToString(). log? ignore?
    }
}
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • Thank you @ToddMenier for your advice. I manage to put each json attribute in different file and load them from my desktop. I realize that I have a problem reading a string that contains HTML information on it. Once I commented that line I manage to read the entire thing at least using Json.Net. I'm going to open a new quation and accept this answer as the solution since the problem is not Flurl, but rather how to read HTML as a string withing the Json. – CodeAndvil Jul 29 '19 at 15:22
  • Just for the record, here is the new [post](https://stackoverflow.com/questions/57265440/problem-with-json-deserialization-of-html-string-using-json-net) with the real issue. – CodeAndvil Jul 30 '19 at 15:34