web-dev-qa-db-ja.com

引用符で囲まれた文字列ではなく、Json.Netを使用して、WCFレストサービス(.NET 4)からjsonを返すにはどうすればよいですか?

UPDATE 10/19/2010少し前にこの質問をしたことがありますが、これらの回答に示された回避策はほとんど満足のいくものではなく、これは多くの人にとって一般的な問題です。 WCFには柔軟性がありません。 REST WCFなしのサービスを作成するために独自のオープンソースC#ライブラリを開始しました。詳細については、 restcake.net または rest.codeplex.com を確認してください上記のライブラリEND UPDATE

UPDATE 8/2/2012ASP.NET Web API (以前のWCF Web API、REST WCFの置き換え) Json.NET をデフォルトで使用END UPDATE

DataContractJsonSerializerは、適切に構成されている場合(具体的には、サイクル)に Json.Net が適切に処理される多くのシナリオを処理できません。

サービスメソッドは、特定のオブジェクトタイプ(この場合は [〜#〜] dto [〜#〜] )を返すことができます。この場合、DataContractJsonSerializerが使用されます。メソッドに文字列を返して、Json.Netで自分でシリアル化することができます。問題は、オブジェクトではなくjson文字列を返すとき、クライアントに送信されるjsonが引用符で囲まれていることです。

DataContractJsonSerializerを使用して特定のオブジェクトタイプを返すと、応答は次のようになります。
{"Message":"Hello World"}

Json.Netを使用してJSON文字列を返すと、応答は次のようになります。
"{\"Message\":\"Hello World\"}"

クライアントで結果をeval()またはJSON.parse()する必要はありません。これは、jsonが引用符で囲まれた文字列として返される場合に行う必要があることです。動作が正しいことを理解しています。それはちょうど私が欲しい/必要なものではありません。生のJSONが必要です。サービスメソッドの戻り値の型が文字列ではなくオブジェクトである場合の動作。

だから、どうすればメソッドにオブジェクト型を返させることができますが、notDataContractJsonSerializerを使用できますか?代わりにJson.Netシリアライザーを使用するように指示するにはどうすればよいですか?

または、応答ストリームに直接書き込む方法がありますか?だから私は自分で生のjsonを返すことができますか?ラッピング引用符なし?

参考のために、私の不自然な例を次に示します。

[DataContract]
public class SimpleMessage
{
    [DataMember]
    public string Message { get; set; }
}

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PersonService
{
    // uses DataContractJsonSerializer
    // returns {"Message":"Hello World"}
    [WebGet(UriTemplate = "helloObject")]
    public SimpleMessage SayHelloObject()
    {
        return new SimpleMessage("Hello World");
    }

    // uses Json.Net serialization, to return a json string
    // returns "{\"Message\":\"Hello World\"}"
    [WebGet(UriTemplate = "helloString")]
    public string SayHelloString()
    {
        SimpleMessage message = new SimpleMessage() { Message = "Hello World" };
        string json = JsonConvert.Serialize(message);
        return json;
    }

    // I need a mix of the two.  Return an object type, but use the Json.Net serializer.
}
40
Samuel Meacham

私はついにこれに対する解決策を見つけました。それは私が好むものではありません(特定のオブジェクトタイプを返し、WCFにDataContractJsonSerializerの代わりにJson.Netシリアライザーを使用するように指示することです)が、うまく機能しており、簡単で明確です。

この新しいソリューションを使用して、不自然な例を拡張します。

_[WebGet(UriTemplate = "hello")]
public void SayHello()
{
    SimpleMessage message = new SimpleMessage() {Message = "Hello World"};
    string json = JsonConvert.Serialize(message);
    HttpContext.Current.Response.ContentType = "application/json; charset=utf-8";
    HttpContext.Current.Response.Write(json);
}
_

voidの戻り型に注意してください。 DataContractJsonSerializerでシリアル化されるため、何も返しません。代わりに、応答出力ストリームに直接書き込みます。戻り値の型はvoidであるため、処理パイプラインはcontent-typeをデフォルトの型「application/json」に設定しないため、明示的に設定します。

これはHttpContextを使用するため、サービスクラスに[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]がある場合にのみ機能すると推測します。これにより、サービスへの要求がASP.NETパイプラインを通過するようになります。 asp.netとの互換性がないと、wcfホスティングはホストに依存しないため、HttpContextは使用できません。

このメソッドを使用すると、GET要求のfirebugで結果が完璧に見えます。引用符で囲まれていない、正しいコンテンツタイプ、正しいコンテンツ長、および生のJSON。そして、Json.Netを使用してシリアル化を取得しています。両方の長所。

私のサービスメソッドに入力パラメーターとして[DataContract]オブジェクトタイプがある場合、deシリアル化に関してどのような障害に遭遇する可能性があるかについて100%肯定的ではありません。 DataContractJsonSerializerもそのために使用されると想定しています。私がそれに来たときにその橋を渡る...それが問題を作成する場合。私の簡単なDTOでは、まだそうではありません。

[〜#〜] update [〜#〜] Olegの回答(UPDATE2の部分)を参照してください。彼は、サービスメソッドの戻り値の型をvoidから_System.ServiceModel.Channels.Message_に変更し、HttpContext.Current.Response.Write()を使用する代わりに、以下を使用します。

_return WebOperationContext.Current.CreateTextResponse (json,
    "application/json; charset=utf-8", Encoding.UTF8);
_

これは確かに優れたソリューションです。オレグ、ありがとう。

PDATE 2これを実現する別の方法があります。サービスの戻りタイプをメッセージからストリームに変更し、これを返します。

_WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json));
_

具体的なテストは行っていませんが、大量のデータを返す可能性のあるメソッドには、これがより良い選択になる可能性があります。しかし、それが非バイナリデータにとって重要かどうかはわかりません。とにかく、考え。

40
Samuel Meacham

正しくないDataContractJsonSerializerを使用しているようです。奇妙なのは、public SimpleMessage SayHelloObject()メソッドに_ResponseFormat = ResponseFormat.Json_属性を定義していないことです。

さらに、文字列に_{"Message":"Hello World"}_があり、デバッガーで表示すると、_"{\"Message\":\"Hello World\"}"_として表示されるため、string json = JsonConvert.Serialize(message);(Json.Net)とまったく同じように表示されます。したがって、どちらの場合も同じ結果が得られるように思えます。

これを確認するには、結果を読み取るクライアントソフトウェアを使用します。いくつかの例を見る

httpget webmethod(c#)へのjQuery ajax呼び出しが機能しない

ContentTypeがJSONでない場合、.asmx WebサービスからJSONを返すことができますか?

AJAX WebService? に送信するJSONオブジェクトを作成する方法

[〜#〜] updated [〜#〜]:コードでメソッドSayHelloString()を定義します。結果は文字列です。メソッドを呼び出すと、この文字列はもう一度JSONシリアル化されます。文字列_{"Message":"Hello World"}_のJSONシリアル化は、引用符付き文字列( http://www.json.org/ オブジェクトではなく文字列の定義を参照)または文字列_"{\"Message\":\"Hello World\"}"_。したがって、Webサービスの両方のメソッドですべてが正しいです。

UPDATED 2:私の答えの「更新」の部分からのヒントが、ダブルJSONシリアル化の切り替えに役立ったことを嬉しく思います。

それにもかかわらず、WCFの概念をさらに維持するために、ソリューションを少し変更することをお勧めします。

WCFでWeb応答のカスタムエンコーディングを実装する場合( http://msdn.Microsoft.com/en-us/library/ms734675.aspx を参照)、WCFメソッドはMessageの代わりにvoid

_[WebGet(UriTemplate = "hello")]
public Message SayHello()
{
    SimpleMessage message = new SimpleMessage() {Message = "Hello World"};
    string myResponseBody = JsonConvert.Serialize(message);
    return WebOperationContext.Current.CreateTextResponse (myResponseBody,
                "application/json; charset=utf-8",
                Encoding.UTF8);
}
_

別のメッセージフォーマッターを使用することもできます。たとえば、CreateStreamResponse(またはその他の http://msdn.Microsoft.com/en-us/library/system.servicemodel.web.weboperationcontext_methods(v = VS.100).aspxCreateTextResponseの代わりに。追加のHTTPヘッダーまたはHttpステータスコードを設定する場合(エラーの場合など)、次の方法でこれを行うことができます。

_OutgoingWebResponseContext ctx = WebOperationContext.Current.OutgoingResponse;
ctx.StatusCode = HttpStatusCode.BadRequest;
_

最後に、コメントから質問を繰り返します。DataContractJsonSerializerの代わりに_Json.Net_を使用する理由を説明してください。パフォーマンスの改善ですか? DateTimeのようないくつかのデータ型のシリアル化を、DataContractJsonSerializerとは異なる方法で実装する必要がありますか?または、_Json.Net_を選択した主な理由は他にありますか?

11
Oleg