我怎样才能从我的WCFrest服务(.NET 4),使用Json.Net,而不是一个string,用引号包装返回JSON?

更新10/19/2010我知道前一段时间我问过这个问题,但是这些答案中显示的解决方法几乎不能令人满意,这对于很多人来说仍然是一个常见问题。 WCF只是不灵活。 我创build了自己的开源C#库,用于在不使用WCF的情况下创buildREST服务。 检查restcake.net或rest.codeplex.com的信息在上述图书馆。 END UPDATE

更新2012年8月2日 ASP.NET Web API (以前WCF Web API,replaceREST WCF)默认情况下使用Json.NET END UPDATE

DataContractJsonSerializer无法处理Json.Net正确configuration(特别是循环)时处理的很多scheme。

一个服务方法可以返回一个特定的对象types(在这种情况下是一个DTO ),在这种情况下, DataContractJsonSerializer将被使用,或者我可以让该方法返回一个string,并使用Json.Net自己完成序列化。 问题是,当我返回一个JSONstring,而不是一个对象,发送到客户端的JSON包装在引号。

使用DataContractJsonSerializer ,返回一个特定的对象types,响应是:
{"Message":"Hello World"}

使用Json.Net来返回一个JSONstring,响应是:
"{\"Message\":\"Hello World\"}"

我不想在客户端上得到eval()或者JSON.parse()这个结果,如果json以stringforms返回,用引号括起来,这就是我所要做的。 我意识到行为是正确的; 这不是我想要/需要的。 我需要原始的json; 服务方法的返回types是对象的行为,而不是string。

那么,如何让我的方法返回一个对象types,但使用DataContractJsonSerializer? 我怎样才能告诉它使用Json.Net序列化器呢?

或者,有没有办法直接写入响应stream? 所以我可以自己回来原始的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. } 

我终于想出了一个解决scheme。 这不是我所喜欢的(这将是返回特定的对象types,并以某种方式指示WCF使用Json.Net序列化程序,而不是DataContractJsonSerializer),但它工作的很好,而且很简单明了。

使用这个新的解决scheme扩展我的人为的例子:

 [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的返回types。 我们不会返回任何东西,因为它会被DataContractJsonSerializer序列化。 相反,我直接写入响应输出stream。 由于返回types是void,所以处理pipe道不会将content-type设置为默认的“application / json”types,所以我明确地设置了它。

因为这使用HttpContext ,所以我猜测只有在你的服务类上有[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]才会起作用,因为这会强制请求服务通过ASP.NETpipe道。 没有asp.net兼容性,HttpContext将不可用,因为wcf托pipe应该是主机不可知的。

使用这种方法,结果在GET请求萤火虫看起来是完美的。 正确的内容types,正确的内容长度和原始json,不包含在引号中。 而且,我正在使用Json.Net来获得序列化。 两全其美。

当我的服务方法将[DataContract]对象types作为input参数时,对于序列化可能遇到什么障碍,我并不是100%肯定的。 我假设DataContractJsonSerializer也将被使用。 当我来的时候会跨过这座桥…如果它产生了一个问题。 到目前为止,还没有我的简单的DTOs。

更新请参阅Oleg的答案(UPDATE2部分)。 他将服务方法的返回types从void更改为System.ServiceModel.Channels.Message ,而不是使用HttpContext.Current.Response.Write() ,他使用:

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

这确实是一个更好的解决scheme。 谢谢奥列格。

更新2还有另一种方法来完成这一点。 将消息的返回types更改为stream,然后返回:

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

我没有做任何特定的testing,但这可能是一个更好的select方法,可能会返回大量的数据。 我不知道是否对非二进制数据很重要。 无论如何,一个想法。

在我看来,你使用不正确的DataContractJsonSerializer 。 奇怪的是:您没有为public SimpleMessage SayHelloObject()方法定义ResponseFormat = ResponseFormat.Json属性。

而且,如果你在一个string中有{"Message":"Hello World"} ,并将其显示在debugging器中,它将显示为"{\"Message\":\"Hello World\"}"string json = JsonConvert.Serialize(message); (Json.Net)。 所以在我看来,你们在这两种情况下都有相同的结果。

要validation这个使用客户端软件读取结果。 看一些例子

jQuery ajax调用httpget webmethod(c#)不起作用

如果ContentType不是JSON,我可以从.asmx Web服务返回JSON吗?

我如何构build一个JSON对象发送到AJAX WebService?

更新 :在你的代码中你定义了方法SayHelloString() 。 结果是一个string。 如果你调用这个方法,这个string将会再次被JS​​ON序列化。 string{"Message":"Hello World"} JSON序列化是一个带引号的string(请参见http://www.json.org/不是对象的定义,而是一个string),或者是string"{\"Message\":\"Hello World\"}" 。 因此,Web服务的两种方法都是正确的。

更新2 :我很高兴我的答案的“更新”部分的提示帮助你的双JSON序列化swich。

不过,我build议你稍微改变一下WCF概念的解决scheme。

如果您希望在WCF中实现Web响应的自定义编码(请参阅http://msdn.microsoft.com/zh-cn/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); } 

您可以使用另一个消息formater:例如CreateStreamResponse (或其他一些请参阅http://msdn.microsoft.com/en-us/library/system.servicemodel.web.weboperationcontext_methods(v = VS.100) .aspx )而不是CreateTextResponse 。 如果你想设置一些额外的HTTP头或Http状态码(例如在出现一些错误的情况下),你可以这样做:

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

最后,我想从一个评论中重复我的问题:你能解释为什么要使用Json.Net而不是DataContractJsonSerializer ? 性能提高了吗? 你需要以DataContractJsonSerializer其他方式实现一些数据types的实现序列化吗? 或者您selectJson.Net是其他?