How can I return json from my WCF rest service (.NET 4) using Json.Net, without its quoted string?

UPDATE 10/19/2010 I know that I asked this question some time ago, but the workarounds shown in these answers are hardly satisfactory, and this is still a common problem for many. WCF is simply not flexible. I started my own open source C # library to create REST services without WCF. Check out restcake.net or rest.codeplex.com for information on this library. END UPDATE

UPDATE 8/2/2012 ASP.NET Web API (formerly WCF Web API, replacing WCF REST) ​​uses Json.NET by default END UPDATE

DataContractJsonSerializer cannot handle many scenarios in which Json.Net only handles a fine when properly configured (in particular, loops).

A service method can either return a specific type of object (in this case DTO ), in which case DataContractJsonSerializer used, or I can force the method to return a string, and serialization itself with Json.Net. The problem is that when I return the json string, unlike the object, the json that is sent to the client is enclosed in quotation marks.

Using a DataContractJsonSerializer , returning a specific type of object, the response will be as follows:
{"Message":"Hello World"}

Using Json.Net to return a json string, the answer will be as follows:
"{\"Message\":\"Hello World\"}"

I do not want to have a result on the client for eval () or JSON.parse (), and this is what I would need to do if json returns as a string wrapped in quotation marks. I understand that the behavior is correct; it's just not what i want / need. I need raw json; behavior when the return type of a service method is an object, not a string.

So, how can I return a method to an object type method but not use a DataContractJsonSerializer? How can I say to use the Json.Net serializer instead?

Or, can anyone write a response stream directly? So I can just return raw json? No wrap quotes?

Here is my contrived example, for reference:

 [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. } 
+37
rest serialization wcf
Jun 11 '10 at
source share
2 answers

I finally figured out how to solve this. This is not what I would prefer (to return a specific type of object and somehow instruct WCF to use the Json.Net serializer instead of the DataContractJsonSerializer), but it works great and is simple and straightforward.

Extending my contrived example with this new solution:

 [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); } 

Note the void return type. We are not returning anything since it will be serialized using the DataContractJsonSerializer. Instead, I write directly to the response output stream. Since the return type is not valid, the processing pipeline does not set the content type to the default type "application / json", so I set it explicitly.

Since this uses an HttpContext , I assume that it will only work if you have [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] in your service class, as this will force service requests to pass through the ASP.NET pipeline. Without asp.net compatibility, HttpContext will not be available, as wcf hosting is considered host agnostic.

Using this method, the results look perfect in firebug for GET requests. The correct content type, the correct content length, and raw json, rather than wrapped in quotation marks. And I get the serialization that I want to use Json.Net. The best of both worlds.

I am not 100% sure what obstacles may arise regarding deserialization when my maintenance methods have DataContract object types as input parameters. I assume that DataContractJsonSerializer will also be used for this. I will cross this bridge when I come to it ... if this creates a problem. So far this is not the case with my simple DTOs.

UPDATE See Oleg's answer (part of UPDATE2). It changes the return type of the service method from void to System.ServiceModel.Channels.Message , and instead of using HttpContext.Current.Response.Write() it uses:

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

This is truly the best solution. Thank you Oleg.

UPDATE 2 There is another way to achieve this. Change the type of service returned from the message to the stream and return it:

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

I have not conducted any specific tests, but it is possible that this is the best choice for methods that can potentially return large amounts of data. However, I do not know if it matters for non-binary data. In any case, a thought.

+36
Jun 12 '10 at 7:30
source share

It seems to me that you are using the wrong DataContractJsonSerializer . Which is strange: you do not define the ResponseFormat = ResponseFormat.Json attribute for the public SimpleMessage SayHelloObject() method.

Also, if the line contains {"Message":"Hello World"} and displays it in the debugger, it will be displayed as "{\"Message\":\"Hello World\"}" , so you see string json = JsonConvert.Serialize(message); (Json.Net). Therefore, it seems to me that in both cases you have the same results.

To verify this, use client software that reads the results. See Examples

Jquery ajax call httpget webmethod (C #) not working

Can I return JSON from the .asmx web service if ContentType is not JSON?

How to create a JSON object to send to AJAX WebService?

UPDATED . In the code, you define the SayHelloString() method. The result is a string. If you call the method, this string will be serialized again by JSON. JSON serialization of the string {"Message":"Hello World"} is a quoted string (see http://www.json.org/ definition is not for an object, but for a string) or exactly the string "{\"Message\":\"Hello World\"}" . So, everything is correct with both methods of your web service.

UPDATED 2 . I am glad that my tip from the β€œUpdate” part of my answer helped you do double JSON serialization.

However, I would recommend that you change your mind a bit to stay more in the WCF concept.

If you want to implement custom encoding of the web response in WCF (see http://msdn.microsoft.com/en-us/library/ms734675.aspx ), your WCF method should better return Message instead of 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); } 

You can use another message CreateStreamResponse : for example CreateStreamResponse (or some others see http://msdn.microsoft.com/en-us/library/system.servicemodel.web.weboperationcontext_methods(v=VS.100).aspx ) instead of CreateTextResponse . If you want to set some additional HTTP headers or an Http status code (for example, in case of some error), you can do this as follows:

 OutgoingWebResponseContext ctx = WebOperationContext.Current.OutgoingResponse; ctx.StatusCode = HttpStatusCode.BadRequest; 

In the end, I want to repeat my question from the comment: could you explain why you want to use Json.Net instead of DataContractJsonSerializer ? Is this a performance improvement? You need to serialize some data types, such as DateTime , otherwise, how to do DataContractJsonSerializer ? Or is the main reason for your choice of Json.Net some other?

+10
Jun 12 2018-10-12T00:
source share



All Articles