MCSD Certification ToolkitBook sales these days are heavily driven by reviews and this book hasn't gotten enough. In an attempt to generate more reviews, I'm giving away 5 copies of the book. Here are some details:
Exam 70-483: Programming in C#
$59.99, 648 pages, 2013
Wrox
ISBN 13: 978-1118612095
By: Tiberiu Covaci, Rod Stephens, Vincent Varallo, Gerry O'Brien
// Draw the new circle if we are drawing one. if (DrawingCircle) { // Make it a circle. int diameter = Math.Max( Math.Abs(StartX - EndX), Math.Abs(StartY - EndY)); e.Graphics.DrawEllipse(Pens.Red, Math.Min(StartX, EndX), Math.Min(StartY, EndY), diameter, diameter); }Here StartX and StartY are the coordinates of the point where the user pressed the mouse down. EndX and EndY are the mouse's current location during the click-and-drag. This code uses the maximum of the selected area's width and height as the circle's diameter. It uses the minimum X and Y coordinates of the selected area as the upper left corner for the circle and draws the circle. The rest of the code that selects a circle is fairly straightforward. Download the example to see the details. The more interesting code is in the following GetCircleFormula method.
// Get the equation for this circle. private void GetCircleFormula(RectangleF rect, out float dx, out float dy, out float r) { dx = rect.X + rect.Width / 2f; dy = rect.Y + rect.Height / 2f; r = rect.Width / 2f; }As its name implies, this method calculates the circle's formula. The figure to the right shows the geometry of a circle. The GetCircleFormula method finds the center of the circle (Dx, Dy). It divides the circle's width by 2 to get the radius. Now the program can display the equation in a Label.
// Draw the circle if we have one. if (GotCircle) { // Fill the circle. e.Graphics.FillEllipse(Brushes.LightBlue, Circle); // Plot the circle's equation. ListFirst the code fills the circle with light blue. Next the program makes variable x go from the circle's minimum X value to its maximum X value. It uses the function with the positive root to generate points along the top half of the circle. (Vocabulary word for the day: A radicand is the value inside a radical symbol.) Then the program makes variable x go from the circle's maximum X value down to its minimum X value. It uses the function with the negative root to generate points along the bottom half of the circle. The program generates these points in order of decreasing X value so they begin where the points on the top half of the circle left off. Finally the program draws the points as a polygon. You can see in the first picture that the blue outline fits the light blue interior of the circle so we know the formula is correct.points = new List (); for (float x = Dx - R; x <= Dx + R; x++) { float radicand = x - Dx; radicand = R * R - radicand * radicand; if (radicand >= 0f) { points.Add(new PointF( x, (float)(Dy + Math.Sqrt(radicand)))); } } for (float x = Dx + R; x >= Dx - R; x--) { float radicand = x - Dx; radicand = R * R - radicand * radicand; if (radicand > 0f) { points.Add(new PointF( x, (float)(Dy - Math.Sqrt(radicand)))); } } e.Graphics.DrawPolygon(Pens.Blue, points.ToArray()); }
// The user's ellipses. private ListThis code defines lists to hold the ellipses and their colors. The Drawing, StartPoint, and EndPoint variables are used to let the user click and drag to create a new ellipse. The Transform and InverseTransform variables are the matrices used to transform the drawing and inverse transform mouse coordinates. Finally Wxmin, Wxmax, Wymin, and Wymax store the world coordinates used to draw the ellipses. When the picCanvas PictureBox resizes, the following code executes.Ellipses = new List (); private List Colors = new List (); // Used while drawing a new ellipse. private bool Drawing = false; private PointF StartPoint, EndPoint; // The transformations. private Matrix Transform = null, InverseTransform = null; private const float DrawingScale = 50; // The world coordinate bounds. private float Wxmin, Wxmax, Wymin, Wymax;
// Create new transformations to center the drawing. private void picCanvas_Resize(object sender, EventArgs e) { CreateTransforms(); picCanvas.Refresh(); }This code calls the following CreateTransforms method and then refreshes the PictureBox.
// Create the transforms. private void CreateTransforms() { // Make the draw transformation. (World --> Device) Transform = new Matrix(); Transform.Scale(DrawingScale, DrawingScale); float cx = picCanvas.ClientSize.Width / 2; float cy = picCanvas.ClientSize.Height / 2; Transform.Translate(cx, cy, MatrixOrder.Append); // Make the inverse transformation. (Device --> World) InverseTransform = Transform.Clone(); InverseTransform.Invert(); // Calculate the world coordinate bounds. Wxmin = -cx / DrawingScale; Wxmax = cx / DrawingScale; Wymin = -cy / DrawingScale; Wymax = cy / DrawingScale; }This method makes a new Matrix object named Transform. It uses the object's Scale method to apply a scaling transformation to enlarge the drawing. The code then uses the object's Translate method to add another transformation to the Matrix to center the drawing in the PictureBox. That completes the drawing transformation. It first scales and then translates the drawing. Now the code makes a clone of the drawing transformation and calls the new Matrix object's Invert method to invert it. This is the transformation that maps from mouse coordinates into world coordinates. (Basically it does the opposite of whatever the drawing transformation does.) The method finishes by calculating the minimum and maximum X and Y coordinates that will appear in the drawing area. (It uses them to decide how long to draw the axes.) The following code shows how the program uses the drawing transformation.
// Draw. private void picCanvas_Paint(object sender, PaintEventArgs e) { // If we don't have the transforms yet, get them. if (Transform == null) CreateTransforms(); e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; e.Graphics.Transform = Transform; // Use a pen that isn't scaled. using (Pen thin_pen = new Pen(Color.Black, 0)) { // Draw the axes. float tic = 0.25f; thin_pen.Width = 2 / DrawingScale; e.Graphics.DrawLine(thin_pen, Wxmin, 0, Wxmax, 0); for (int x = (int)Wxmin; x <= Wxmax; x++) e.Graphics.DrawLine(thin_pen, x, -tic, x, tic); e.Graphics.DrawLine(thin_pen, 0, Wymin, 0, Wymax); for (int y = (int)Wymin; y <= Wymax; y++) e.Graphics.DrawLine(thin_pen, -tic, y, tic, y); // Draw the ellipses. thin_pen.Width = 0; for (int i = 0; i < Ellipses.Count; i++) { using (Brush brush = new SolidBrush(Color.FromArgb(128, Colors[i]))) { e.Graphics.FillEllipse(brush, Ellipses[i]); } thin_pen.Color = Colors[i]; e.Graphics.DrawEllipse(thin_pen, Ellipses[i]); } // Draw the new ellipse. if (Drawing) { thin_pen.Color = Color.Black; e.Graphics.DrawEllipse(thin_pen, Math.Min(StartPoint.X, EndPoint.X), Math.Min(StartPoint.Y, EndPoint.Y), Math.Abs(StartPoint.X - EndPoint.X), Math.Abs(StartPoint.Y - EndPoint.Y)); } } }If the program hasn't created the transformations yet, it calls CreateTransforms to do so now. Next the program sets the Graphics object's SmoothingMode property to get a smooth picture. It also sets the object's Transform property to the drawing transformation matrix. The code then creates a Pen with width 0. That width tells the program to draw with the thinnest line possible. (If you don't do this, then the pen is scaled by the drawing transformation so the ellipses are drawn with huge edges.) The rest of the method is reasonably straightforward. It draws the axes and then loops through the ellipses drawing them. If the program is in the middle of drawing a new ellipse, the method finishes by drawing it. The following code shows how the program uses the inverse transformation to map from mouse (device) coordinates to world coordinates.
// Convert from device coordinates to world coordinates. private PointF DeviceToWorld(PointF point) { PointF[] points = { point }; InverseTransform.TransformPoints(points); return points[0]; }The Matrix class provides a TransformPoints method that transforms an array of points by applying the its transformation. The DeviceToWorld method takes a point in device coordinates as a parameter. It creates an array holding that point and calls the inverse transformation matrix's TransformPoints method to transform the point to world coordinates. It then returns the converted point. The rest of the program's code is fairly straightforward. The mouse events that let the user click and drag call the DeviceToWorld method to convert mouse coordinates into device coordinates. For example, the following code shows the PictureBox's MouseDown event handler.
// Let the user draw a new ellipse. private void picCanvas_MouseDown(object sender, MouseEventArgs e) { Drawing = true; // Get the start and end points. StartPoint = DeviceToWorld(e.Location); EndPoint = StartPoint; }This is just like any other click and drag mouse event except it calls DeviceToWorld. Download the example to see the rest of the code.
// Send the message. private void btnSend_Click(object sender, EventArgs e) { try { string carrier_email = cboEmail.SelectedItem.ToString(); string phone = txtPhone.Text.Trim().Replace("-", ""); phone = phone.Replace("(", "").Replace(")", "").Replace("+", ""); string to_email = phone + "@" + carrier_email; SendEmail(txtToName.Text, to_email, txtFromName.Text, txtFromEmail.Text, txtHost.Text, int.Parse(txtPort.Text), chkEnableSSL.Checked, txtPassword.Text, txtSubject.Text, txtBody.Text); MessageBox.Show("Message sent"); } catch (Exception ex) { MessageBox.Show(ex.Message); } }The program first gets the SMS carrier's email address from the cboEmail ComboBox. The email address the program uses must not contain any characters other than digits so the code trims the address to remove spaces. It also removes the -, (, ), and + characters. Next the code appends the @ symbol and the carrier's SMS gateway email address. The result should look something like this:
[email protected]The program finishes by calling the SendEmail method described in the second post mentioned above to send the message to this email address. That's all there is to it! The tools you need to download the JSON carrier data, parse the data, and send the email message are somewhat involved. Once you've built those tools, however, sending the SMS message is comparatively easy.
using System.Net; using System.Net.Mail; // Send an email message. private void SendEmail(string to_name, string to_email, string from_name, string from_email, string host, int port, bool enable_ssl, string password, string subject, string body) { // Make the mail message. MailAddress from_address = new MailAddress(from_email, from_name); MailAddress to_address = new MailAddress(to_email, to_name); MailMessage message = new MailMessage(from_address, to_address); message.Subject = subject; message.Body = body; // Get the SMTP client. SmtpClient client = new SmtpClient() { Host = host, Port = port, EnableSsl = enable_ssl, UseDefaultCredentials = false, Credentials = new NetworkCredential(from_address.Address, password), }; // Send the message. client.Send(message); }The method creates MailAddress objects to represent the From and To email addresses. You can use strings for these when you make the MailMessage but then you can't associate a human-friendly name (like "Rod Stephens") with the email addresses. Next the program creates a MailMessage, passing its constructor the From and To addresses. The code then sets the MailMessage's Subject and Body fields. The method then creates an SmtpClient object. It sets the client's host and port so it knows where to send the email. The method sets the EnableSsl property according to the value it was passed as a parameter. If your email server uses SSL (Secure Sockets Layer--Gmail uses this), check the box on the form so this is set to true. The code also sets UseDefaultCredentials to false and sets the client's Credentials property to a new NetworkCredential object containing your email user name and password. Finally the method calls the SmtpClient's Send method to send the email. The System.Net.Mail.MailMessage class supports some other features that you might want to use. For example, it has CC and Bcc properties that let you send courtesy copies and blind courtesy copies. The version shown here is good enough for the next post, which sends an SMS message. The following code shows how the program calls the SendEmail method.
// Send the message. private void btnSend_Click(object sender, EventArgs e) { try { SendEmail(txtToName.Text, txtToEmail.Text, txtFromName.Text, txtFromEmail.Text, txtHost.Text, int.Parse(txtPort.Text), chkEnableSSL.Checked, txtPassword.Text, txtSubject.Text, txtBody.Text); MessageBox.Show("Message sent"); } catch (Exception ex) { MessageBox.Show(ex.Message); } }This code simply calls SendEmail passing it the values you entered on the form. Note that the form's initial values for host and port (smtp.gmail.com and 587) work for Gmail. If you want to use some other email host, you'll need to change those values. The next post will bring together the results of this post and the previous JSON posts to send an SMS message.
{ "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.
{ "City":"Bugsville", "EmailAddresses": [ "[email protected]", "[email protected]", "[email protected]" ], "Name":"Rod Stephens", "Orders": [ {"Description":"Pencils, dozen","Price":1.13,"Quantity":10}, {"Description":"Notepad","Price":0.99,"Quantity":3}, {"Description":"Cookies","Price":3.75,"Quantity":1} ], "PhoneNumbers": [ {"Key":"Home","Value":"111-222-3333"}, {"Key":"Cell","Value":"222-333-4444"}, {"Key":"Work","Value":"333-444-5555"} ], "State":"CA", "Street":"1337 Leet St", "Zip":"98765" }Some of the fields such as City, Name, and State are simple string values. The EmailAddresses value is an array of strings. The Orders value is an array of Order objects, each of which has a Description, Price, and Quantity. (Really this should be a collection of Order objects, each of which has a collection of OrderItem objects that have Description, Price, and Quantity properties, but the structure shown here is complicated enough already.) The PhoneNumbers value is a dictionary where the keys are phone number types (Home, Cell, Work) and the values are the phone number strings. This example builds a Customer object. It then serializes it into a JSON format and then deserializes it to re-create the original object. The following code shows the Order class.
// Add a reference to System.Runtime.Serialization. using System.Runtime.Serialization; namespace howto_use_json { [Serializable] public class Order { [DataMember] public string Description; [DataMember] public int Quantity; [DataMember] public decimal Price; // Return a textual representation of the order. public override string ToString() { decimal total = Quantity * Price; return Description + ": " + Quantity.ToString() + " @ " + Price.ToString("C") + " = " + total.ToString("C"); } } }To allow the program to serialize Order objects, the class must be marked with the Serializable attribute. That attributes is defined in the System.Runtime.Serialization namespace so the code includes a using statement to make using that namespace easier. To use the namespace, you also need to add a reference to System.Runtime.Serialization at design time. The fields within the class are marked with the DataMember attribute so the serializer knows to serialize them. The last part of the class is a ToString method that simply returns a textual representation of the object's values. The following code shows the Customer class.
// Add a reference to System.Runtime.Serialization. using System.Runtime.Serialization; // Add a reference to System.ServiceModel.Web. using System.Runtime.Serialization.Json; using System.IO; namespace howto_use_json { [Serializable] public class Customer { [DataMember] public string Name = ""; [DataMember] public string Street = ""; [DataMember] public string City = ""; [DataMember] public string State = ""; [DataMember] public string Zip = ""; [DataMember] public Dictionary<string, string> PhoneNumbers = new Dictionary<string, string>(); [DataMember] public List<string> EmailAddresses = new List<string>(); [DataMember] public Order[] Orders = null; ... (Other code shown later) ... } }This class starts much as the Order class does. It also uses the System.Runtime.Serialization namespace so it includes the corresponding using statement. The class also uses the System.Runtime.Serialization.Json namespace (which requires you to add a reference to System.ServiceModel.Web) and the System.IO namespaces so it also includes using statements for them. The class's definition starts by defining some simple properties such as Name, Street, and City. The class defines the PhoneNumbers member as a Dictionary<string, string>. It defines the EmailAddresses member as List<string>. The serializer automatically converts these into the appropriate JSON types. To make serializing and deserializing objects easier, the class includes the methods ToJson and FromJson. The ToJson method shown in the following code returns a JSON representation of an object.
// Return the JSON serialization of the object. public string ToJson() { // Make a stream to serialize into. using (MemoryStream stream = new System.IO.MemoryStream()) { // Serialize into the stream. DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Customer)); serializer.WriteObject(stream, this); stream.Flush(); // Get the result as text. stream.Seek(0, SeekOrigin.Begin); using (StreamReader reader = new StreamReader(stream)) { return reader.ReadToEnd(); } } }This method creates a MemoryStream in which to write. It then creates a DataContractJsonSerializer object to serialize the Customer object. It calls the serializer's WriteObject method to write the current Customer object into the stream and flushes the stream. Next the code rewinds the stream to the beginning, creates an associated StreamReader, and uses the reader's ReadToEnd method to get the serialization out of the stream and return it. (This seems needlessly awkward to me. If you find a simpler method, email me. The static FromJson method shown in the following code takes a JSON serialization of a Customer object, uses it to re-create the object, and returns the new object.
// Create a new Customer from a JSON serialization. public static Customer FromJson(string json) { // Make a stream to read from. MemoryStream stream = new MemoryStream(); StreamWriter writer = new StreamWriter(stream); writer.Write(json); writer.Flush(); stream.Position = 0; // Deserialize from the stream. DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Customer)); Customer cust = (Customer)serializer.ReadObject(stream); // Return the result. return cust; }This method creates a MemoryStream and an associated StreamWriter. It uses the writer to write the JSON serialization into the stream. It then flushes the writer and rewinds the stream. Next the code creates a DataContractJsonSerializer object to deserialize the Customer object. It calls the serializer's ReadObject to read the object's serialization from the stream, casts the result into a Customer object, and returns the object. The ToString method shown in the following code is the last piece of the Customer class. It simply returns a textual representation of the Customer object.
// Return a newline separated text representation. public override string ToString() { // Display the basic information. string result = "Customer" + Environment.NewLine; result += " " + Name + Environment.NewLine; result += " " + Street + Environment.NewLine; result += " " + City + " " + State + " " + Zip + Environment.NewLine; // Display phone numbers. result += " Phone Numbers:" + Environment.NewLine; foreach (KeyValuePairThe ToString method is a bit long but reasonably straightforward. The following shows how the main program demonstrates JSON serialization by serializing and deserializing a Customer object.pair in PhoneNumbers) { result += " " + pair.Key + ": " + pair.Value + Environment.NewLine; } // Display email addresses. result += " Email Addresses:" + Environment.NewLine; foreach (string address in EmailAddresses) { result += " " + address + Environment.NewLine; } // Display orders. result += " Orders:" + Environment.NewLine; foreach (Order order in Orders) { result += " " + order.ToString() + Environment.NewLine; } return result; }
private void Form1_Load(object sender, EventArgs e) { // Make an object to serialize. Customer cust = new Customer() { Name = "Rod Stephens", Street = "1337 Leet St", City = "Bugsville", State = "CA", Zip = "98765", PhoneNumbers = new DictionaryThe form's Load event handler starts by initializing a Customer object complete with phone numbers, email addresses, and Order objects. Next the code calls the Customer object's ToJson method to get its serialization and display it in the txtJson TextBox. (Notice that the JSON serialization in the picture doesn't include nice line breaks and formatting to align the data nicely. It sacrifices that to save space.) The code then calls the Customer class's FromJson method to deserialize the serialization and create a new Customer object. It finishes by using the new object's ToString method to show its properties in the txtProperties TextBox. This example may seem complicated but it's actually not too bad. It's a bit longer than it might otherwise be because the Customer and Order classes include several useful data types such as a list, dictionary, and array of objects. The actual JSON code in the ToJson and FromJson methods is somewhat confusing but it's fairly short. Those methods also make using JSON to serialize and deserialize objects easy in practice.() { {"Home", "111-222-3333"}, {"Cell", "222-333-4444"}, {"Work", "333-444-5555"}, }, EmailAddresses = new List () { "[email protected]", "[email protected]", "[email protected]", }, }; cust.Orders = new Order[3]; cust.Orders[0] = new Order() { Description = "Pencils, dozen", Quantity = 10, Price = 1.13m }; cust.Orders[1] = new Order() { Description = "Notepad", Quantity = 3, Price = 0.99m }; cust.Orders[2] = new Order() { Description = "Cookies", Quantity = 1, Price = 3.75m }; // Display the serialization. string serialization = cust.ToJson(); txtJson.Text = serialization; txtJson.Select(0, 0); // Deserialize. Customer new_cust = Customer.FromJson(serialization); txtProperties.Text = new_cust.ToString(); txtProperties.Select(0, 0); }
// Download and display the text file. private void Form1_Load(object sender, EventArgs e) { const string url = "https://raw.github.com/cubiclesoft/email_sms_mms_gateways/master/sms_mms_gateways.txt"; txtFile.Text = GetTextFile(url); txtFile.Select(0, 0); }The code defines the URL of the file to download. It then calls the GetTextFile method described next to download the file and displays the result in the txtFile TextBox. The following code shows the GetTextFile method.
using System.IO; using System.Net; ... // Get the text file at a given URL. private string GetTextFile(string url) { try { url = url.Trim(); if (!url.ToLower().StartsWith("http")) url = "http://" + url; WebClient web_client = new WebClient(); MemoryStream image_stream = new MemoryStream(web_client.DownloadData(url)); StreamReader reader = new StreamReader(image_stream); string result = reader.ReadToEnd(); reader.Close(); return result; } catch (Exception ex) { MessageBox.Show("Error downloading file " + url + '\n' + ex.Message, "Download Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } return ""; }The code first uses the string class's Trim method to remove leading and trailing whitespace from the URL (just in case). Then if the URL doesn't begin with "http," the code adds "http://" to the beginning of the URL. (The code only checks for "http" so it can also handle "https" as in the URL used by this example.) Next the code creates a WebClient object. It uses the client's DownloadData method to get the file. DownloadData returns an array of bytes so the program creates a memory stream associated with the array and then makes a StreamReader to read the memory stream. The code calls the reader's ReadToEnd method and closes the reader. Finally it returns the string read by the StreamReader. There are several ways this method could fail (file doesn't exist, improper permissions, not connected to the network, network failure, and so on) so the code does all its work within a try-catch block. With a it more work, you can download other files such as pictures or RTF format files to display more than just text. If you really want a fancy announcement page downloaded from the internet, you might be better off placing a WebBrowser control on the form and making it navigate to a web page. If you examine the file that this example downloads, you'll see that it contains JSON-formatted data listing SMS gateway email addresses. My next blog post provides a brief introduction to JSON. The post after that explains how to read the data in the text file that this example downloads and displays.