deserialize api response w/newtonsoft

Viewed 449

I need help deserializing a response. (I'm making a call to Data247 on a phone number, and the api returns the mobile phone carrier). The api call is working and I'm getting back a valid response that looks like this:

{
    "response": {
        "status": "OK",
        "results": [
            {
                "phone": "11234567890",
                "wless": "y",
                "carrier_name": "Blue Licenses Holding, LLC",
                "carrier_id": 6,
                "sms_address": "1234567890@txt.att.net",
                "mms_address": "1234567890@mms.att.net"
            }
        ]
    }
}

This is in a C#.NET winforms app. To test this, you'd have to have an access key from Data247. Everything is working through the api call. My problem is with this line:

Data247 data247 = JsonConvert.DeserializeObject<Data247>(response.Content);

...and so I don't think my class is structured correctly. ...or maybe I'm not deserializing correctly. My code looks like this:

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Diagnostics;

using RestSharp;
using Newtonsoft.Json;

namespace myTest
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        private void btnVerify_Click(object sender, EventArgs e)
        {
            try
            {
                string request = txtEndpoint.Text;
                request += "?key=" + txtAccessKey.Text;
                request += "&api=CU";
                request += "&phone=" + txtPhone.Text;
                var client = new RestClient(request);
                var response = client.Execute(new RestRequest());
                Data247 data247 = JsonConvert.DeserializeObject<Data247>(response.Content);

                MessageBox.Show(response.Content);
                Debug.Print(response.Content);
            }
            catch (Exception ex)
            {
                Debug.Print(ex.ToString());
            }
        }

        public class Data247
        {
            Response response { get; set; }
            public class Response
            {
                public string status { get; set; }
                Results results { get; set; }

                public class Results
                {
                    List<Phone> phones = new List<Phone>();
                    public Phone phone { get; set; }
                    public class Phone
                    {
                        public string phone { get; set; }
                        public string wless { get; set; }
                        public string carrier_name { get; set; }
                        public string carrier_id { get; set; }
                        public string sms_address { get; set; }
                        public string mms_address { get; set; }
                    }
                }
            }
        }
    }
}
2 Answers

Besides the recommended trick from @Hirasawa, I believe it's important to understand how all this works, so I would like to point out a couple of glitches in your code.

You don't need the Phone class.

If you look at the structure of the data, the phone number is only a property of the result item. Also, Results is an array, so let's update the code to fix those errors:

public class Data247
{
    Response response { get; set; }

    public class Response
    {
        public string status { get; set; }

        public Results[] results { get; set; }

        public class Results
        {
            public string phone { get; set; }
            public string wless { get; set; }
            public string carrier_name { get; set; }
            public string carrier_id { get; set; }
            public string sms_address { get; set; }
            public string mms_address { get; set; }
        }
    }
}

Target properties must be public.

In order to correctly deserialize the response, the properties in your target classes must be public. Fixing those errors we get:

public class Data247
{
    public Response response { get; set; }

    public class Response
    {
        public string status { get; set; }

        public Results[] results { get; set; }

        public class Results
        {
            public string phone { get; set; }
            public string wless { get; set; }
            public string carrier_name { get; set; }
            public string carrier_id { get; set; }
            public string sms_address { get; set; }
            public string mms_address { get; set; }
        }
    }
}

With the above changes, the code now works. Well, almost ;-). It turns out that we now have a nested class Results at the same level of a property named Results. This is not valid in C# and the compiler will let you know that with an error: "There is already a Results member declared.".

The fix is easy though, we just move the nested classes to the outer scope or we could rename the Response and Results classes to something like ResponseDTO and ResultDTO, but I find the former easier to do. So now our code is:

public class Data247
{
    public Response response { get; set; }
}

public class Response
{
    public string status { get; set; }

    public Results[] results { get; set; }
}

public class Results
{
    public string phone { get; set; }
    public string wless { get; set; }
    public string carrier_name { get; set; }
    public string carrier_id { get; set; }
    public string sms_address { get; set; }
    public string mms_address { get; set; }
}

BONUS: Write idiomatic C# code.

If you want to write idiomatic code, you should use the recommended property names. This is a matter of style and the code as it is is working, but a C# developer will thank you if your code is properly written.

This is a brief summary of what I'll do:

  1. Rename property names to UpperCamelCase format. Eg: mms_address should be MmsAddress.
  2. Due to 1. we must also use attributes to map the actual property names in the json to the C# property name.

Below is the final code:

public class Data247
{
    [JsonProperty("Response")]
    public Response Response { get; set; }
}

public class Response
{
    [JsonProperty("status")]
    public string Status { get; set; }

    [JsonProperty("results")]
    public Results[] Results { get; set; }
}

public class Results
{
    [JsonProperty("Phone")]
    public string Phone { get; set; }

    [JsonProperty("wless")]
    public string Wless { get; set; }

    [JsonProperty("carrier_name")]
    public string CarrierName { get; set; }

    [JsonProperty("carrier_id")]
    public string CarrierId { get; set; }

    [JsonProperty("sms_address")]
    public string SmsAddress { get; set; }

    [JsonProperty("mms_address")]
    public string MmsAddress { get; set; }
}

Hope this helps!

Related