How to extract only some of the information from a JSON file in C#

Recall from my post Anatomy of an example that my original goal with this series of articles was to write a program to send an SMS message. To do that, I want to download a file listing SMS gateway email addresses. The example Download and display a text file whenever a program starts in C# shows how to download the file.

That file is a JSON format file. The example Use JSON to serialize and deserialize objects in C# shows how to use JSON to serialize and deserialize objects. You could use that technique to deserialize the SMS gateway file. You would define an object that represents the kinds of data contained in the SMS gateway file and then simply deserialize the file.

After staring for quite a while at the file, I decided that this would be fairly hard. It's a big file with a complex structure and it contains lots of information that I don't really need, so I decided on a different approach. (One that also makes a good blog post.)

Instead of trying to understand the structure of the entire JSON file, this example reads it without really knowing what it represents and then sifts through it to find the data it needs.

The following text shows the structure of the JSON file (with LOTS of data omitted).

{
        "info" : "JSON array ...",
        "license" : "MIT or LGPL...",
        "lastupdated" : "2012-07-01",
        "countries" : {
                "us" : "United States",
                "ca" : "Canada",
                "ar" : "Argentina",
                "aw" : "Aruba",
            ...
        },
        "sms_carriers" : {
                "us" : {
                        "airfire_mobile" : ["Airfire Mobile",
                            "{number}@sms.airfiremobile.com"],
                        "alltel" : ["Alltel", "{number}@message.alltel.com"],
                        ...
                        "at_and_t_mobility" : ["AT&T Mobility (Cingular)",
                            "{number}@txt.att.net",
                            "{number}@cingularme.com",
                            "{number}@mobile.mycingular.com"],
                        ...
                },
                "ca" : {
                        "aliant" : ["Aliant", "{number}@chat.wirefree.ca"],
                        ...
                },
            ...
        },
        "mms_carriers" : {
                "us" : {
                        "alltel" : ["Alltel", "{number}@mms.alltel.com"],
                        ...
                },
                ...
        }
}

Notice that the us/at_and_t_mobility carrier supports three email addresses. Normally you can use the first one if a carrier has more than one email address, but the program displays them all in case you know which one you want to use.

The following code shows the two classes that the program uses to store the information it gets from the file.

public class CarrierInfo
{
    public string CarrierAbbreviation, CarrierName;
    public List<string> Emails = new List<string>();

    public override string ToString()
    {
        return CarrierName;
    }
}

public class CountryInfo
{
    public string CountryAbbreviation, CountryName;
    public List<CarrierInfo> Carriers = new List<CarrierInfo>();

    public override string ToString()
    {
        return CountryName;
    }
}

The CarrierInfo class stores a cell phone carrier's abbreviation and name, and a list of supported SMS gateway email addresses.

The CountryInfo class stores a country's abbreviation and name, and a list of carriers that are available in that country.

The code that reads the data is fairly long but not super complicated. Basically it loads the data into a dictionary where the keys are strings and the values are objects. Many of the values are also dictionaries with a similar structure.

For example, the file's top-level data is stored in a dictionary with keys info, license, lastupdated, countries, sms_carriers, and mms_carriers. The sms_carriers entry is a dictionary with keys us, ca, and other country abbreviations. Each of the entries in the sms_carriers dictionary is another dictionary with keys that are carrier abbreviations and with values that are arrays holding a carrier's name and email addresses.

The following code shows how the program reads the data.

// Add a reference to System.Web.Extensions.dll.
using System.Web.Script.Serialization;

using System.IO;
using System.Net;
...
private void Form1_Load(object sender, EventArgs e)
{
    // Get the data file.
    const string url = "https://raw.github.com/cubiclesoft/" +
        "email_sms_mms_gateways/master/sms_mms_gateways.txt";
    string serialization = GetTextFile(url);

    // Add a reference to System.Web.Extensions.dll.
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    Dictionary<string, object> dict =
        (Dictionary<string, object>)serializer.DeserializeObject(serialization);

    // Get the countries.
    Dictionary<string, CountryInfo> country_infos =
        new Dictionary<string, CountryInfo>();
    Dictionary<string, object> countries =
        (Dictionary<string, object>)dict["countries"];
    foreach (KeyValuePair<string, object> pair in countries)
    {
        CountryInfo country_info = new CountryInfo()
            { CountryAbbreviation = pair.Key, CountryName = (string)pair.Value };
        country_infos.Add(country_info.CountryAbbreviation, country_info);
    }

    // Get the SMS carriers.
    Dictionary<string, object> sms_carriers =
        (Dictionary<string, object>)dict["sms_carriers"];
    foreach (KeyValuePair<string, object> pair in sms_carriers)
    {
        // Get the corresponding CountryInfo.
        CountryInfo country_info = country_infos[pair.Key];

        // Get the country's carriers.
        Dictionary<string, object> carriers =
            (Dictionary<string, object>)pair.Value;
        foreach (KeyValuePair<string, object> carrier_pair in carriers)
        {
            // Create a CarrierInfo for this carrier.
            CarrierInfo carrier_info = new CarrierInfo()
                { CarrierAbbreviation = carrier_pair.Key };
            country_info.Carriers.Add(carrier_info);
            object[] carrier_values = (object[])carrier_pair.Value;
            carrier_info.CarrierName = (string)carrier_values[0];
            for (int email_index = 1; email_index <
                carrier_values.Length; email_index++)
            {
                string email = (string)carrier_values[email_index];
                carrier_info.Emails.Add(email.Replace("{number}", ""));
            }
        }
    }

    // Display the countries.
    cboCountry.Items.Clear();
    foreach (CountryInfo country in country_infos.Values)
    {
        cboCountry.Items.Add(country);
    }

    // Make an initial selection.
    cboCountry.SelectedIndex = 0;
}

The program defines the URL where it will get the file. It then uses the GetTextFile method described in Download and display a text file whenever a program starts in C# to get the file.

Next the code creates a JavaScriptSerializer. Unlike the serializers described in the previous example that serializes and deserializes objects, this serializer doesn't know what kind of object it is deserializing.

The program call's the serializer's DeserializeObject method. That method returns a Dictionary that contains strings associated with objects. The objects hold various kinds of data depending on what's in the JSON file.

This example gets the "countries" entry from the dictionary. This entry is another dictionary that contains the abbreviations and names of countries used by the JSON file.

The program loops through the country dictionary's key/value pairs. For each country pair, the program stores the country's abbreviation and name in a CountryInfo object. It stores the new CountryInfo object in a dictionary named country_infos using the country's abbreviation as the key.

Next the program returns to the dictionary that represents the JSON file's highest level of data and gets the sms_carriers entry in that dictionary. This entry is another dictionary that holds information about the carriers that are represented in the file.

The program loops over the carrier information. Because the carriers are grouped by country, the keys in the key/value pairs are country abbreviations. The program uses those to look up the corresponding CountryInfo object in the country_infos dictionary.

Next the program uses the value part of the carrier's key/value pair to get the information about the carrier. It creates a CarrierInfo object and adds it to the appropriate CountryInfo object's Carriers list. Finally the code adds the email addresses for the carrier to the CarrierInfo object's Emails list.

The method then displays the names of the countries it loaded in the cboCountry ComboBox. It finishes by selecting the first country in the ComboBox so the program always has country selected.

The program's remaining code updates its ComboBox's when the user makes a selection. When the user selects a country, the carriers ComboBox displays a list of the carriers available in that country. When the user selects a carrier, the email addresses ComboBox displays a list of email addresses provided by that carrier.

The following code shows how the program responds when the user picks a country.

// Display the selected country's carriers.
private void cboCountry_SelectedIndexChanged(object sender, EventArgs e)
{
    if (cboCountry.SelectedIndex < 0)
    {
        cboCarrier.SelectedIndex = -1;
    }
    else
    {
        // Get the selected CountryInfo object.
        CountryInfo country = cboCountry.SelectedItem as CountryInfo;
        Console.WriteLine("Country: " + country.CountryAbbreviation +
            ": " + country.CountryName);

        // Display the CountryCarrier's carriers.
        cboCarrier.DataSource = country.Carriers;
    }
}

If the user has selected a country, the program converts the selected country into its corresponding CountryInfo object. It then sets the cboCarrier ComboBox's DataSource property to the country's Carriers property. That property is a list of CarrierInfo objects so the ComboBox displays them.

When the user picks a carrier from the carriers ComboBox, the following code executes.

// Display the selected carrier's emails addresses.
private void cboCarrier_SelectedIndexChanged(object sender, EventArgs e)
{
    if (cboCarrier.SelectedIndex < 0)
    {
        cboEmail.SelectedIndex = -1;
    }
    else
    {
        // Get the selected CarrierInfo object.
        CarrierInfo carrier = cboCarrier.SelectedItem as CarrierInfo;
        Console.WriteLine("Carrier: " + carrier.CarrierName);

        // Display the Carrier's email addresses.
        cboEmail.DataSource = carrier.Emails;
    }
}

This code converts the selected carrier into its CarrierInfo object. It then sets the email address ComboBox's DataSource property to the CarrierInfo object's Emails property so it displays the list of available email addresses.

The program doesn't do anything when the user selects an email address.

At this point, you can download the SMS gateway file and get the information you need out of it to find an SMS gateway email address. The next step is to send email to that address. The next two posts explain how to do that.

   

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments
  • No comments exist for this post.
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.