Bakulog

獏の夢日記的な何か。

C#で気象情報REST API(WeatherHacks)を使ってみる

備忘録兼ねて。

 

WeatherHacksというのは商用利用でなければ自由に利用可能な気象情報APIサービスです。本記事では特にREST API(仕様はココに記載)をC#プログラムでサクッと使う方法を紹介します。

 

なお本記事は内容が「C#で天気予報のWeb API(Weather Hacks)を使ってみる~コンソールアプリケーション」とかなーり激しく被っているのですが、当該記事ではJsonのパースにNewtonsoftを使っておらず、個人的にはC#JSON捌くならNewtonsoft使う方がデファクトスタンダードだろうという認識で居るため本記事の公開に至っています。

 

では本題。まずVisual Studio 2015を用意し、適当にコンソールアプリケーションを作成します。次にJSONをパースするためにNewtonsoft.JsonをNuGetパッケージマネージャからインストールします。

※普段NuGet使わない方のために補足しますが、パッケージのインストールを行うにはソリューションエクスプローラの「参照」で右クリックして「NuGetパッケージの管理…」を選択してパッケージ管理画面を開き、「参照」でオンラインから「Newtonsoft.Json」を検索するとロケットみたいなアイコンのパッケージが出るので、それをインストールすればOKです。

 

準備が出来たらコードを書きます。ぶっちゃけ「拾ってパースする」でおしまいなので非常にシンプルです。

(※追記: 投稿時はいささか冗長なコードを載せていたので修正しました。需要不明ですが修正前のバージョンは記事の最下部に追記として残しています。)

using System;
using System.IO;
using System.Net.Http;
using Newtonsoft.Json.Linq;

namespace WeatherHacksApiClientTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string baseUrl = "http://weather.livedoor.com/forecast/webservice/json/v1";
            //東京都のID
            string cityname = "130010";

            string url = $"{baseUrl}?city={cityname}";
            string json = new HttpClient().GetStringAsync(url).Result;
            JObject jobj = JObject.Parse(json);

            File.WriteAllText(
                Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "tokyo_weather.txt"),
                jobj.ToString()
                );

            string todayWeather = (string)((jobj["forecasts"][0]["telop"] as JValue).Value);
            Console.WriteLine(todayWeather);
        }
    }
}


上のサンプルではJSONの取得結果をNewtonsoftのJObjectとしてパースし、デスクトップフォルダに"tokyo_weather.txt"という名前で保存します。

プログラム中でJSONのデータを扱う方法については最後あたりに書いてあるように、Newtonsoftではデータ構造に対応して辞書式あるいは配列型のアクセスを行えば具体的にJSONの中身を取得できます。上記の例では今日の天気(「晴れ」とか「曇時々雨」とか)を取得しています。データ構造については実際にプログラムを動かして保存したデータを見るか、APIの仕様ページに載っているサンプルを参考にしてください。

 

ということで、C#でも(他の多くの言語と同様)REST API叩くくらいサラッと出来ますという紹介でした。今回は以上です。

 

追記1:

記事投稿のほぼ直後にguitarrapc_techさんが関連コードとしてGithubにコードを公開してくださいました。ありがとうございます!

それで基本的には本追記の内容は「Github側のコード参考になるので是非見て下さい」という話なんですが、そのGithubコードを拝見した中でもC#JSONという括りで紹介したい事が一つあったので、それについて追記します。

本記事のサンプルコードでは「JSONはJObjectとして保持してプログラム中で使いましょー」みたいな指針を紹介しましたが、この方針だとデータへの辞書式アクセスの部分でいまいち信頼性に欠けるという欠点があります。具体的に言うと、上記サンプルで天気予報の情報を取る際にjobj["forecasts"]jobj["forecast"]typoしていても実行前に気づくのはムリでしょう、と。

 

この問題をラクに解決する方法としては「【C#】JSON(orXML)からクラスを自動生成」に載っているように、VSの機能を用いてJSONからC#のクラス定義を作る方法があります。コレでクラス定義を作っておくと、上でやっているJSONオブジェクトのパース処理は次のように書き換えることが出来ます(Githubの例ではココがそうです)。

using Newtonsoft.Json;
//..

static void Main(string[] args)
{
    string baseUrl = "http://weather.livedoor.com/forecast/webservice/json/v1";
    //東京都のID
    string cityname = "130010";

    string url = $"{baseUrl}?city={cityname}";
    string json = new HttpClient().GetStringAsync(url).Result;
    WeatherData weather = JsonConvert.DeserializeObject<WeatherData>(json);
    //何かしらの処理
}


//↓JSONコピペで定義されるクラス(クラス名とかは適宜書き換える)
public class WeatherData
{
    public Pinpointlocation[] pinpointLocations { get; set; }
    //...
}

//子要素のクラスも必要な分だけ定義される
//...

見た通りですがJsonConvertを使ってオブジェクトをデシリアライズしています。ノリ自体はXmlSerializerとかとよく似ていますね。

(大概の場合はそうだと思いますが)レスポンスのデータ構造がキッチリ決まっているAPIではコチラの方法の方が安定するので良さげかなあと思います。

 

追記2:

上記の追記で紹介させて頂いたGithub側のコードを拝見した所サンプルコードが冗長だったのに気づき、本文側のコードを直しました。コチラには修正前の冗長なコードを反省として残しています。

以下のコードではWebRequestを使ってAPIのレスポンスを拾ってますが、今回のようなREST APIに対しては無駄に凝った手順となっています。WebRequest(とかHttpWebRequest)はもっと複雑な処理が要る場面で使うものです。

using System;
using System.Text;
using System.IO;
using System.Net;
using Newtonsoft.Json.Linq;

namespace WeatherHacksApiClientTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string url = "http://weather.livedoor.com/forecast/webservice/json/v1";
            //東京都のID
            string cityname = "130010";

            string reqUrl = $"{url}?city={cityname}";
            var req = WebRequest.Create(reqUrl);
            var stream = req.GetResponse().GetResponseStream();
            var jsonSb = new StringBuilder();
            using (var sr = new StreamReader(stream))
            {
                jsonSb.Append(sr.ReadToEnd());
            }
            var jobj = JObject.Parse(jsonSb.ToString());
            File.WriteAllText(
                Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "tokyo_weather.txt"), 
                jobj.ToString()
                );

            string todayWeather = (string)((jobj["forecasts"][0]["telop"] as JValue).Value);
            Console.WriteLine(todayWeather);
        }
    }
}

こちらではJSON相当のStringBuilderを得るまでに7行も食ってるので絵的にだいぶアレな感じになっていますね。こういうコードを書かないように気を付けましょう。