简体   繁体   中英

Deserialization error from RESTful API

Getting an error saying

JsonReaderException: Unexpected character encountered while parsing value: M. Path ', line 0, position 0.

when try to parse JSON response (not entire, but first few items, I changed in the xxxxxx's for anonymity):

{
  "total_count": 1091,
  "items": [
    {
      "hap": "retry_limit_reached",
      "message": "Retry limit reached. Dropped: postmaster@xxxxx.ca \u2192 hamnifold@cxxxxxxxxems.com 'REMINDER - Pattison Stampede BBQ (Jul 11, 2018)' No MX for crscranessystems.com Server response: 498 No MX for crscranessystems.com",
      "type": "error",
      "created_at": "Thu, 05 Jul 2018 06:45:48 GMT",
      "message_id": "20180704185046.1.F263522F7F8571FE@mxxxxxx.ca"
    },
    {
      "hap": "tempfailed",
      "message": "Will retry in 14400 seconds: postmaster@xxxxxxxx.ca \u2192 conor@xxxxxxxxxy.org 'REMINDER - Pattison Stampede BBQ (Jul 11, 2018)' No MX for greexxxxxxxy.org Server response: 498 No MX for grexxxxxxy.org",
      "type": "warn",
      "created_at": "Thu, 05 Jul 2018 06:45:17 GMT",
      "message_id": "20180704225015.1.E917B878FC275EE7@mxxxxxxx.ca"
    },
    {
      "hap": "opened",
      "message": "Opened: jordan@lixxxxxxxxs.com",
      "type": "info",
      "created_at": "Thu, 05 Jul 2018 06:45:15 GMT",
      "message_id": "20180704192612.1.856579D071F84CF8@xxxxxxx.ca"
    },
    {
      "hap": "tempfailed",
      "message": "Will retry in 14400 seconds: postmaster@mxxxxxxx.ca \u2192 ccrawford@xxxxxx.com 'SUBJECT' No MX for step-eps.com Server response: 498 No MX for stxxxxxx.com",
      "type": "warn",
      "created_at": "Thu, 05 Jul 2018 06:45:14 GMT",
      "message_id": "20180704225014.1.33D047AC8A685236@xxxxxxxxi.ca"
    },
    //...

Here is part of my code:

if(response.IsSuccessStatusCode)
{
    ViewBag.Msg = "Success";
    var stresult = await response.Content.ReadAsStringAsync();                
    var flogs = JsonConvert.DeserializeObject<List<EmailLog>>(stresult);
    logs = flogs.ToList();
}

Here is the emaillog model:

public class EmailLog
{
    public string hap { get; set; }

    public string message { get; set; }

    public string type { get; set; }

    public string created_at { get; set; }

    public string message_id { get; set; }
}

This is the first time I have ever used RESTful API and JSON, so I might be missing something easy.

EDIT:

This is my full controller code which makes the API request:

HttpClient client = new HttpClient();

            client.BaseAddress = new Uri("https://api:key-xxxxxxxxxxxxxxxxxxxxxxxxxxxx@api.mailgun.net/v3");

            List<EmailLog> logs = new List<EmailLog>();

            HttpResponseMessage response = await client.GetAsync("/mg.xxxxx.ca/log");
            if (response.IsSuccessStatusCode)
            {

                //    var stresult = await response.Content.ReadAsStringAsync();                
                //    var flogs = JsonConvert.DeserializeObject<List<EmailLog>>(stresult);
                //    logs = flogs.ToList();
                var json = await response.Content.ReadAsStringAsync();
                var jObject = JObject.Parse(json);
                if (jObject.ContainsKey("items"))
                {
                    ViewBag.Msg = "Success";
                    logs = jObject["items"].ToObject<List<EmailLog>>();
                }
                //var logs = JsonConvert.DeserializeObject<ApiResponse>(json);

            }

            else { ViewBag.Msg = "Fail"; }

            return View(logs);

The problem is trying to get a list of EmailLog objects directly because they are enclosed within the "items" element of your json response.

You can solve it by using a wrapper class, for example:

public class ApiResponse
{
    [JsonProperty(PropertyName = "total_count")]
    public int TotalCount { get; set; }
    [JsonProperty(PropertyName = "items")]
    public List<EmailLog> Items { get; set; }
}

And then you don't need to use a list to deserialize:

var flogs = JsonConvert.DeserializeObject<ApiResponse>(json);

Here is an alternative to creating a root object as already explained in another answer

This approach takes advantage of the Linq To JSON provided by the Json.Net, which is being used to deserialize the JSON.

if(response.IsSuccessStatusCode) {
    ViewBag.Msg = "Success";
    var json = await response.Content.ReadAsStringAsync();
    var jObject = JObject.Parse(json);
    if (jObject.ContainsKey("items"))
        logs = jObject["items"].ToObject<List<EmailLog>>();   
}

The JSON is parsed and only the desired key is extracted.

This assumes that value of "total_count" was not needed so only extracts the items value.

If the entire object is wanted then defining a root object and deserializing to that would be the easier approach, as demonstrated in @Isma's answer

UPDATE :

Based on discussion you indicated that the service is currently returning

"Mailgun Magnificent API"

which would match the invalid character exception being thrown trying to parse M , as it is clearly not well formatted JSON.

After reviewing the Mailgun documentation it looks like the URL you are using will only work in a browser and was not meant to be used the HttpClient class.

The documentation explains that the request should be made using BASIC authorization

For example.

var myMailgunDomain = "https://api.mailgun.net/v3/mg.xxxxx.ca/";            
var client = new HttpClient() {
    BaseAddress = new Uri(myMailgunDomain)
};
var apiKey = "api:key-xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var parameter = Convert.ToBase64String(Encoding.ASCII.GetBytes(apiKey));
client.DefaultRequestHeaders
    .Authorization = new AuthenticationHeaderValue("Basic", parameter);
HttpResponseMessage response = await client.GetAsync("log");
//...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM