[[Open棟梁Project>http://opentouryo.osscons.jp/]] - [[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]] * 目次 [#ec67115c] #contents *概要 [#zb9de49d] [[WCF]]や[[WebAPI]]で、指定のREST Formatに適合するJSONを返す方法を説明する。 *はじめに [#vf3529ba] **出力したいJSONを確認する [#dbc5c33d] まず、どのようなJSONを出力したいのかを確認します。 これがなければ、始まりません。 **出力したいJSONに合わせて、クラスやプロパティを定義する [#e10b9cbb] 次に、出力したいJSONに合わせて、クラスやプロパティを定義します。 ***基本 [#i16a4292] 基本的には、以下のルールに従って、クラスやプロパティを定義します。 -JSONオブジェクト ({ キー名 : 値 }) の場合 --「キー名」を名前に持つプロパティを定義する -JSON配列 ([ 値1, 値2, ... ]) の場合 --リストまたは配列のプロパティを定義する -キー名が不定の場合 --キー名が不定 (実行時に決まる) の場合、事前のクラス/プロパティ定義ができない。~ --このようなときは、Dictionary<TKey, TValue>を、Dictionary<string, object> として定義する。 ***ルール [#t6d4fac1] |出力したいJSON|定義するクラス・プロパティ|h |{ "key1" : "value1", "key2" : "value2" }|public class Sample&br;{&br; ''string key1'' { get; set; }&br; ''string key2'' { get; set; }&br;}| |[ "1", "2", "3" ]|''List<string>'' listData { get; set; }| |{ "key1" : [ "1", "2", "3" ] }|public class Sample&br;{&br; ''List<string> key1'' { get; set; }&br;}| |{ "key" : { "key1" : "value1", "key2" : "value2" } }|public class Sample&br;{&br; ''string key1'' { get; set; }&br; ''string key2'' { get; set; }&br;}&br;public class Sample2&br;{&br; ''Sample key'' { get; set; }&br;}| |[ { "key1" : "value1", "key2" : "value2" }, &br;{ "key1" : "value3", "key2" : "value4" } ]|public class Sample&br;{&br; ''string key1'' { get; set; }&br; ''string key2'' { get; set; }&br;}&br;''List<Sample>'' listData { get; set; }| ***BeanとDictionaryの違い [#y3f29fd0] BeanとDictionaryでSerializeされるJSONのformatが異なる。 -Bean にマップする場合 { "Prop1": "Value1", "Prop2": "Value2" } -Dictionary<string, string> にマップする場合 [ { "Key": "Prop1", "Value": "Value1" }, { "Key": "Prop2", "Value": "Value2" } ] *JSONを返すサービスを作成する [#n81c4388] **WCFの場合 [#wdb092a0] WCFでJSONを返す場合、既定ではDataContractJsonSerializerが使用されます。 ***既定のDataContractJsonSerializerを使用してJSONを返す方法 [#c008c778] -WCFサービスを作成し、コントラクト部分にWebGet属性を追加します。ポイントは以下の2点です。 --ResponseFormatに、WebMessageFormat.Jsonを指定し、JSONを返すサービスであることを宣言する --サービスメソッドの戻り値の型に、先ほど定義したクラス/プロパティの型を指定する -サービスを実装します。 [ServiceContract] public interface IJSONService { [OperationContract] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetJson")] Sample GetJson(); } public class JSONService : IJSONService { public Sample GetJson() { // JSON にシリアライズする元となるオブジェクトを作成する Sample sample = new Sample() { StringKey = "StringValue", IntKey = 123, ListKey = new List<string>() { "List1", "List2", "List3" } }; // クライアントにデータを返す return sample; } } public class Sample { public string StringKey { get; set; } public int IntKey { get; set; } public List<string> ListKey { get; set; } } -作成したWCFサービスを、RESTサービスとして公開するための設定をweb.configに行います <system.serviceModel> <services> <service name="WebApplication1.JSONService"> <endpoint address="" binding="webHttpBinding" behaviorConfiguration="MyBehavior" contract="WebApplication1.IJSONService" /> </service> </services> <behaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="MyBehavior"> <webHttp/> </behavior> </endpointBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel> -"http://~/JSONService.svc/GetJson" にリクエストを送ったときの実行結果 {"IntKey":123,"ListKey":["List1","List2","List3"],"StringKey":"StringValue"} -既定のDataContractJsonSerializerの注意点~ 既定のDataContractJsonSerializerでは、Dictionary型のオブジェクトを正しく扱えません。~ Dictionary型のオブジェクトを扱う場合は、[http://www.newtonsoft.com/json JSON.NET]やJavaScriptSerializerなど、その他のSerializerを使用する必要があります。その方法は、次に紹介します。 ***DataContractJsonSerializer以外のSerializerを使用してJSONを返す方法 [#t49e9049] -WCFサービスを作成し、コントラクト部分にWebGet属性を追加します。ポイントは以下の2点です。 --ResponseFormatには何も指定しない --サービスメソッドの戻り値の型は、System.ServiceModel.Channels.Messageを指定する~ (SOAPのMessage BodyにJSONを直接書き込む) -サービスを実装します。 [ServiceContract] public interface IJSONService2 { [OperationContract] [WebGet(UriTemplate = "GetJson")] Message GetJson(); } public class JSONService2 : IJSONService2 { public Message GetJson() { // JSON にシリアライズする元となるオブジェクトを作成する Sample2 sample = new Sample2() { key = new Dictionary<string, string>() { {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"} } }; // JSON.NET を使用して JSON 形式にシリアライズ string jsonStr = JsonConvert.SerializeObject(sample); // 'X-Content-Type-Options: nosniff' ヘッダーを追加する WebOperationContext.Current.OutgoingResponse.Headers.Add("X-Content-Type-Options", "nosniff"); // JSON を返す return WebOperationContext.Current.CreateTextResponse(jsonStr, "application/json; charset=utf-8", Encoding.UTF8); } } public class Sample2 { public Dictionary<string, string> key { get; set; } } -作成したWCFサービスを、RESTサービスとして公開するための設定をweb.configに行います <system.serviceModel> <services> <service name="WebApplication1.JSONService2"> <endpoint address="" binding="webHttpBinding" behaviorConfiguration="MyBehavior" contract="WebApplication1.IJSONService2" /> </service> </services> <behaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="MyBehavior"> <webHttp/> </behavior> </endpointBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel> -"http://~/JSONService2.svc/GetJson" にリクエストを送ったときの実行結果 {"key":{"key1":"value1","key2":"value2","key3":"value3"}} **ASP.NET Web APIの場合 [#paf23e5b] ASP.NET Web APIでは、既定でJSON.NETによってSerializeされます。~ このため、Dictionary型も問題なくSerializeできます。 -Webアプリケーションに、Web APIを作成します。 public class ValuesController : ApiController { // GET api/values public Sample Get() { // JSON にシリアライズする元となるオブジェクトを作成する Sample sample = new Sample() { key = new Dictionary<string, string>() { {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"} } }; // JSON を返す return sample; } } public class Sample { public Dictionary<string, string> key { get; set; } } -"http://~/api/Values" にリクエストを送ったときの実行結果 {"key":{"key1":"value1","key2":"value2","key3":"value3"}} -ASP.NET Web APIの注意点~ 既定では、ASP.NET Web APIは、HTTPリクエストに含まれるAcceptヘッダーの内容により、クライアントに返すデータの形式が決められます。 --Acceptに''application/xml''が含まれていた場合は、XMLとして返されます。(レスポンスのContent-Typeヘッダーが''application/xml''となる) --Acceptに''application/json''が含まれていた場合は、JSONとして返されます。(レスポンスのContent-Typeヘッダーが''application/json''となる)~ このため、常にJSONとしてデータを受け取りたい場合は、リクエストヘッダーに''Accept: application/json''を必ずつけるようにしてください。 *相互運用 [#nf281504] はやり、Serializer毎に挙動が違うので、~ RESTのAPIのデータ・コントラクトは、XMLやJSONのformatレベルで行うのが良いようです。 **Javaでの検証結果 [#h8e9a2e5] ***検証内容 [#la5c4259] 以下を参考に、Tomcat 上で実行する Java の Web API を作成してみました。 -覚えておくといつか必ず役に立つ。Javaで簡単Web API作成。- JAX-RS~ http://qiita.com/ukiuni@github/items/a516439b30c340703292 ***結果結果 [#d4fc2a47] -Bean にマップする場合 { "Prop1": "Value1", "Prop2": "Value2" } >→ .NET と「同じ」挙動 -HashMap<String, String> にマップする場合 { "Prop1": "Value1", "Prop2": "Value2" } >→ .NET と「異なる」挙動 **REST APIのリファレンス [#lcad8d73] 数個、AzureのREST APIをピックアップしてみました。 -List all virtual machines in a resource group~ https://msdn.microsoft.com/en-us/library/azure/mt163572.aspx >GrsourceGroupのnameが RESTのUriに含まれていて、ResponceがJOSN形式で返されます -Start a virtual machine~ https://msdn.microsoft.com/en-us/library/azure/mt163628.aspx >ResponceがHTTP 200 だけのもあります -Create or update a virtual machine~ https://msdn.microsoft.com/en-us/library/azure/mt163591.aspx >呼び出すパラメタが大きく、変更を加える操作は、PUT で行われ、Request Body に パラメタがJOSN形式で渡されます。 やはり、JSONのformatが明記されており、formatレベルでデータ・コントラクトを結ぶ必要があります。