Extract part of a JSON file in C#

[JSON]

This example shows how to read a JSON file and pick out only the pieces that are interesting.

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 that lists 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 in JSON format. 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 at the file for quite a while, 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 that 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.

This example uses classes that are defined in the System.Web.Script.Serialization namespace. To use that namespace, you need to add a reference to the System.Web.Extensions.dll library. (I don’t know why Microsoft can’t make namespaces and library names match.) Unfortunately, that library may not be visible in your Add References dialog.

If you are using a more recent version of Visual Studio, open Project > Properties > Application and make sure that you are targeting the .NET 4 Framework. Then use Project > Add Reference to open the Add Reference dialog, and look on the .NET tab for the library.

If you still can’t find it, or if you are using an older version of Visual Studio (like I am), open the Add Reference dialog, click on the Browse tab, and browse for the library. Depending on which versions of Visual Studio you have installed, you may find it in one of the following places.

  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Web.Extensions.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Web.Extensions.dll

After you add the reference, you can use the System.Web.Script.Serialization namespace.

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 serialize and deserialize specific object types, 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. That 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.

The program then 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 a country selected.

The program’s remaining code updates its ComboBox control’s when you make a selection. When you select a country, the carriers ComboBox displays a list of the carriers available in that country. When you select 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 you 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 you have selected a country, the program converts the selected country into its corresponding CountryInfo object. It then sets the cboCarrier ComboBox control’s DataSource property to the country’s Carriers property. That property is a list of CarrierInfo objects, so the ComboBox displays them.

When you pick 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 control’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 you select 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 in this series explain how to do that.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, files, serialization and tagged , , , , , , , , , , , , , . Bookmark the permalink.

One Response to Extract part of a JSON file in C#

  1. Pingback: How to send an SMS text message in C# - C# HelperC# Helper

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.