C#でHTTP経由でJSONを取得して値を取り出す
サンプル作ったらボツになったのでここに書くことにします。
HTTPで結果をJSONで返すWebサービスがあって、そこにコンソールアプリからアクセスするというものです。
なお、C#はほぼ初心者で、特に.NETはAPIがほとんど分かりません。MSDNとか検索で調べた結果をまとめたものです。
エントリの最後にコードの最終版を載せています。
やりたいこと。
- コンソールアプリ
- コマンドライン引数をパラメータにする(URLエンコード)
- HTTPアクセス
- JSON解析(静的)
環境
- Windows 7 Professional 32bit
- Visual Studio Express 2012 + .NET Framework 4.0
- VSは.NET 4.0 に対応していればこれより古くてもたぶんOK
- JSON直列化(後述)は4.0以降
余談ですが、最近Visual Studioに慣れてきて、使うのが楽しくなってきました。特にVS2010以降はいいですね。
Webアプリ仕様(仮)
こんな感じのアプリを想定しています。
- リクエストURL
http://example.com/app/search?type=json&q=
パラメータqにURLエンコードしたパラメータを付けると、HTTP経由でJSONが返されます。
- JSONの結果サンプル
{ "message" : "メッセージ", "records" : [ { "id" : "A0001", "name" : "社員1", "age" : 21 }, { "id" : "A0002", "name" : "社員2", "age" : 23 }] }
JSONはネスト(入れ子)になっています。
コンソールアプリ
新しいプロジェクトで「C# コンソール アプリケーション」を選択します。
名前は"JsonTest"とします。
.NETのバージョンが".NET Framework 4.0 client profile"になっている場合は、".NET Framework 4.0"に変更します。
デバッグモードでメッセージを出力させる*1には、System.Diagnotics.Debugクラスを使用します。
using System.Diagnotics; // Debug.WriteLine("");
コマンドライン引数をパラメータに
パラメータqには、ブラウザだったらテキストボックスにスペース区切りで入力するように、コマンドライン引数をスペース区切りしたものを指定します。
URLエンコードには、System.Web.HttpUtilityクラスのUrlEncodeメソッドを使用します。これを使用するには、参照設定に"System.Web"を追加する必要があります。文字列結合には、String.Joinメソッドを使用します。
using System.Web; // 参照設定の追加が必要 // 中略 string q = HttpUtility.UrlEncode(string.Join(" ", args));
HTTPアクセス
HTTP送受信を行うには、System.Net.WebRequestクラスのCreateメソッドとGetResponseメソッドを使用します。
using System.Net; // 中略 var req = WebRequest.Create(url + q); req.Headers.Add("Accept-Language:ja,en-us;q=0.7,en;q=0.3"); var res = req.GetResponse(); // res = System.Net.WebResponse
この例では、リクエストヘッダの設定も行っています。
JSON解析(静的)
シリアル化*2を使用して、JSONをオブジェクトに変換することができます。
ここでは、静的な取得方法を使います。動的(dynamic型)に取得する方法もありますが、よく知らないので、今回は静的だけでやります。
これらの機能を使用するには、参照設定に"System.Runtime.Serialization"を追加する必要があります。
まず、構造を表す型を定義します。System.Runtime.SerializationのDataContractAttributeクラスの属性"DataContract"と"DataMember"を指定することで、シリアル化可能であることを宣言できます。
前述のJSONにあてはめると、こんな感じになります。
using System.Collections.Generic; // List using System.Runtime.Serialization; // DataContract,DataMember // 参照設定の追加が必要 // 中略 [DataContract] public class ServiceResult { [DataMember] public string message { get; set; } [DataMember] public List<Record> records { get; set; } [DataContract] public class Record { [DataMember] public string id { get; set; } [DataMember] public string name { get; set; } [DataMember] public uint age { get; set; } } }
逆シリアル化(デシリアライズ)するには、次のようにします。その他のシリアル化については、System.Runtime.Serialization 名前空間を参照してみてください。
var resStream = res.GetResponseStream(); // resStream = System.IO.Streamクラス var serializer = new DataContractJsonSerializer(typeof(ServiceResult)); ServiceResult info = (ServiceResult)serializer.ReadObject(resStream); resStream.Close(); res.Close();
完成版
ここまでのを合体させて、ちょっとだけブラッシュアップして完成です。
ちょっとごちゃごちゃしますが、出力:デバッグ窓に結果が出力されます。
using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Web; namespace JsonTest { class Program { static void Main(string[] args) { // URLエンコーディング string url = "http://example.com/app/search?type=json&q="; string q = HttpUtility.UrlEncode(string.Join(" ", args)); Debug.WriteLine("encoded q: " + q); // HTTPアクセス var req = WebRequest.Create(url + q); req.Headers.Add("Accept-Language:ja,en-us;q=0.7,en;q=0.3"); var res = req.GetResponse(); // レスポンス(JSON)をオブジェクトに変換 ServiceResult info; using (res) { using (var resStream = res.GetResponseStream()) { var serializer = new DataContractJsonSerializer(typeof(ServiceResult)); info = (ServiceResult)serializer.ReadObject(resStream); } } // 結果を出力 Debug.WriteLine("message: " + info.message); Debug.WriteLine("record count: " + info.records.Count); foreach (var r in info.records) Debug.WriteLine(" id={0}, name={1}, age={2}", r.id, r.name, r.age); } } [DataContract] public class ServiceResult { [DataMember] public string message { get; set; } [DataMember] public List<Record> records { get; set; } [DataContract] public class Record { [DataMember] public string id { get; set; } [DataMember] public string name { get; set; } [DataMember] public uint age { get; set; } } } }