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.