Update: This post is part of the Second Annual C# Advent.

Along with koans and katas, several books have helped me knock the rust off after a recent two-year stint as a non-coding team leader. One of the exercises in Brian P. Hogan’s “Exercises for Programmers: 57 Challenges to Developer Your Coding Skills” jumped out at me recently, so I decided to tackle it.

At its simplest, exercise 48 – Grabbing the Weather is a relatively short exercise that asks you to use the OpenWeatherMap API and display weather information.

I decided to create a .NET Core console application. I started out at my beloved command line:



With the application created, I need to make sure it’s in git. It’s not that this is a hugely important application, it’s more about practicing good habits with source code control.

After initializing git, I copied an existing .gitignore file from another .NET project and then added my files and did an initial commit.

At this point, it’s a toss up whether to use Vim, VS Code or Visual Studio, but in this case, I used Visual Studio since it’s what many people are using AND because I have the VsVim extension installed. Best of both worlds!

The requirements for the exercise:

Using the OpenWeatherMap API at http://openweathermap.org/current, create a program that prompts for a city name and returns the current temperature for the city.

Easy enough! The first thing I need to do is visit https://openweathermap.org/current and either sign up or sign in. Sign up is free and takes about a minute. Once you have an account, you’ll need an API key. An account has a default key, so you can simply grab the key and hold on to it OR create a new one. Keep in mind, it might take some time for the key to become active.

I want my console application to call a service that will hide all the details of fetching the weather. Who knows, at some point, I may want to change weather providers and using a service should insulate the client (the console application) from internal code changes.

Instead of a city name, I want my weather service to take in a zip code and return back the a structure with all the data the OpenWeatherMap API returns.

public interface IWeatherFetcher

{

CurrentWeather GetCurrentWeather(string zipCode);

}

In Main, the call will look something like this:

static void Main(string[] args)

{

Console.WriteLine(“Mike’s Weather App”); IWeatherFetcher wf = new WeatherFetcher();

var currentWeather = wf.GetCurrentWeather(“49036”); Console.ReadLine();

}

The service:

public CurrentWeather GetCurrentWeather(string zipCode)

{

var json = RunAsync(“your API key”, zipCode).GetAwaiter().GetResult();

return JsonConvert.DeserializeObject<CurrentWeather>(json);

} private HttpClient client = new HttpClient();

private async Task<string> RunAsync(string key, string zipCode)

{

client.BaseAddress = new Uri(“http://api.openweathermap.org”);

client.DefaultRequestHeaders.Accept.Clear();

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”)); var result = “”;

try

{

result = await GetWeatherAsync(key, zipCode);

}

catch(Exception e)

{

Console.WriteLine(e.Message);

} return result;

} private async Task<string> GetWeatherAsync(string key, string zipCode)

{

var result = “”;

string url = $”/data/2.5/weather?q={zipCode}&units=imperial&appid={key}”; HttpResponseMessage response = await client.GetAsync(url);

if(response.IsSuccessStatusCode)

{

result = await response.Content.ReadAsStringAsync();

} else {

// dump any errors to the screen

Console.WriteLine(response.ToString());

}

return result;

}

To get the CurrentWeather class, I took the original json that was returned and used a special feature in Visual Studio called “Paste JSON as Classes” found under Edit | Paste Special in Visual Studio.

CurrentWeather (the result of “Paste JSON as Classes”):

public class CurrentWeather

{

public Coord coord { get; set; }

public Weather[] weather { get; set; }

public string _base { get; set; }

public Main main { get; set; }

public int visibility { get; set; }

public Wind wind { get; set; }

public Clouds clouds { get; set; }

public int dt { get; set; }

public Sys sys { get; set; }

public int id { get; set; }

public string name { get; set; }

public int cod { get; set; }

} public class Coord

{

public int lon { get; set; }

public float lat { get; set; }

} public class Main

{

public float temp { get; set; }

public int pressure { get; set; }

public int humidity { get; set; }

public float temp_min { get; set; }

public float temp_max { get; set; }

} public class Wind

{

public float speed { get; set; }

public float deg { get; set; }

} public class Clouds

{

public int all { get; set; }

} public class Sys

{

public int type { get; set; }

public int id { get; set; }

public float message { get; set; }

public string country { get; set; }

public int sunrise { get; set; }

public int sunset { get; set; }

} public class Weather

{

public int id { get; set; }

public string main { get; set; }

public string description { get; set; }

public string icon { get; set; }

}

With all that in place, I can now make one more modification to my Main to display some data:

static void Main(string[] args)

{

Console.WriteLine(“Mike’s Weather App”); IWeatherFetcher wf = new WeatherFetcher();

var currentWeather = wf.GetCurrentWeather(“49036″); Console.WriteLine($”The temp in {currentWeather.name} is {currentWeather.main.temp}.”); Console.ReadLine();

}

I can now display any of the information that’s returned. I have had to make a few adjustments to the generated classes because data types coming back didn’t necessarily match what was generated – for example, Wind.deg was generated as an int, but it comes back as a float. Easy fix, but it’ll take some debugging to get it all correct.

This was a fun exercise that took me about 30 minutes to complete and I still have a couple things to do like actually prompt for the city (or Zip Code). One tweak I made but didn’t really talk about it is the inclusion of the API key within the code. I’ll explain that in a follow-up post.

I hope you enjoyed this post! Oh, and check out the code on GitHub!