文字列で作ったJSONをWCFサービスの戻り値として返す


WCFサービスからJSON形式でレスポンスを返す場合、List型Dictionary型を使ってデータを詰めるか、あるいはJSONに対応するプロパティを持たせたクラスを定義するなどして、戻り値の型に設定すると思います。

今回は自前で作ったJSON形式の文字列を、そのままJSONとしてレスポンスに乗せる方法を試してみました。



戻り値にListとDictionaryを使った場合の問題点

Dictionaryにデータを詰めた場合、JSONに変換すると「Key」と「Value」が自動で追加されます。
データを作る側の汎用性はあるんですが、JSONを受け取る側はメンバーに「Key」と「Value」がついていること前提でプログラムを組む必要があります。

※ListとDictionaryでデータを返した場合のJSONの例
{"response":[
    [{"Key":"従業員コード","Value":"1001"},{"Key":"部署名","Value":"総務部"}],
    [{"Key":"従業員コード","Value":"1002"},{"Key":"部署名","Value":"営業部"}],
    [{"Key":"従業員コード","Value":"1003"},{"Key":"部署名","Value":"開発部"}]
]}

これをjavascriptで読んだりすると処理が結構めんどくさいんですよね。
KeyとかValueとか使わずに、"従業員コード"で"1001"をズバッっと取得したいんです。

JSONの形式に合わせたクラスを作った場合の問題点

JSONの形式に合わせたクラスを作る場合は、当然ですがあらかじめJSONの形が決まっていないとダメです。
また、JSONのフォーマットの数だけクラスも作成する必要があり、JSONの項目が可変だったりする場合に対応しきれません。

例えばこんな形式のJSONを返したい場合、
{"response":{
    "従業員コード":"1001",
    "部署名":"総務部"
    }
}

こんなクラスを用意しておく必要があります。
<DataContract()>
Public Class json_response
    <DataMember()>
    Public Property 従業員コード As String
    <DataMember()>
    Public Property 部署名 As String
End Class
JSONの項目が増えたら、
{"response":{
    "従業員コード":"1001",
    "部署名":"総務部",
    "入社年度":2007
    }
}

クラスのプロパティも増やす必要があります。
<DataContract()>
Public Class json_response
    <DataMember()>
    Public Property 従業員コード As String
    <DataMember()>
    Public Property 部署名 As String
    <DataMember()>
    Public Property 入社年度 As Integer  ''追加
End Class

これだとJSONの形が変わったらWCFサービスを毎回ビルドする必要がありますし、通信が増えればJSONに対応したクラスも増やさないといけません。
リクエストごとに戻り値の型が違う場合、WCFサービスのメソッドも増やす必要があります。

JSON形式の文字列をJSONデータとして返す

ResponseFormatWebMessageFormat.Jsonを指定していると、文字列で作ったJSONのダブルクォーテーションにエスケープ文字「\」が追加されてしまって、JSONが崩れてしまいます
なのでResponseFormatには何も指定せず、文字列をレスポンスに直接書き込みます。
また、戻り値の型はString型ではなく、System.ServiceModel.Channels.Messageを使用しています。

例)クエリによってカラムが可変となるデータベースの検索結果を、JSON形式にシリアライズして返す
※シリアライズにはJSON.NETを使用
''IService.vb
Imports System.Runtime.Serialization
Imports System.ServiceModel

<ServiceContract()>
Public Interface IService

    ''ここでResponseFormatは指定しない
    <OperationContract(),
    WebInvoke(Method:="POST" _
              , BodyStyle:=WebMessageBodyStyle.Wrapped _
              , RequestFormat:=WebMessageFormat.Json _
              , UriTemplate:="/jsonsample")>
    Function execSQL(ByVal sql As String) As System.ServiceModel.Channels.Message
    
End Interface
''Service.svc.vb
Imports System.Reflection
Imports System.Runtime.InteropServices

Public Class RestService
    Implements IService

    Function execSQL(ByVal sql As String) As System.ServiceModel.Channels.Message _
        Implements IService.execSQL

        ''DBオープン
        Dim oConn As New OracleConnection
        oConn.ConnectionString = "User Id=XXX;Password=XXX;Data Source=XXX;"
        ''SQL実行
        Dim oCmd As New OracleCommand
        oCmd.Connection = oConn
        oCmd.CommandText = strSqlStatement
        Dim oAdp As New OracleDataAdapter(oCmd)
        Dim ds As New DataSet
        oAdp.Fill(ds, "result_table")
        oAdp.Dispose()
        oCmd.Dispose()
        oConn.Dispose()
        
        ''JSON.NETでシリアライズ
        Dim jsontxt As String = JsonConvert.SerializeObject(ds, Formatting.None)
        ''ヘッダ追加
        WebOperationContext.Current.OutgoingResponse.Headers.Add("X-Content-Type-Options", "nosniff")
        ''シリアライズした文字列をJSONとして返す
        Return WebOperationContext.Current.CreateTextResponse(jsontxt, "application/json; charset=utf-8", Encoding.UTF8)

    End Function
End Class

実行結果の例
{"result_table":[
    {"従業員コード":"1001","部署名":"総務部"},
    {"従業員コード":"1002","部署名":"営業部"},
    {"従業員コード":"1003","部署名":"開発部"}
]}

Dictionaryのように"Key"、"Value"が勝手に付いてこないので、受信側がJSONとして使いやすいと思います。

まとめ

DataSetからJSONへのシリアライズはJSON.NETを使用しましたが、複雑な構造のJSONを送りたい場合は自前で文字列操作してゴリゴリJSONを作らないといけないので、そこが面倒です。

以上

<スポンサーリンク>


0 件のコメント :

コメントを投稿