更新2010年10月19日 我知道我刚才问过这个问题,但这些答案中显示的解决方法并不令人满意,对许多人来说这仍然是一个常见的问题。 WCF只是不灵活。我创建了自己的开源C#库,用于在没有WCF的情况下创建REST服务。有关所述库的信息,请查看restcake.net或rest.codeplex.com。 END UPDATE
更新2012年8月2日 ASP.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;当service方法的返回类型是对象而不是字符串时的行为。
那么,我如何让我的方法返回一个对象类型,但不使用DataContractJsonSerializer?我怎么能告诉它使用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.
}
答案 0 :(得分:40)
我终于想出了解决方案。这不是我想要的(这将是返回特定的对象类型,并以某种方式指示WCF使用Json.Net序列化器,而不是DataContractJsonSerializer),但它工作得很好,而且简单明了。
使用这个新解决方案扩展我的人为例子:
[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兼容性,HttpContext将不可用,因为wcf托管应该是主机不可知的。
使用此方法,结果在gb请求的firebug中看起来很完美。正确的内容类型,正确的内容长度和原始json,不包括在引号中。而且,我正在使用Json.Net获得我想要的序列化。两全其美。
当我的服务方法将[DataContract]对象类型作为输入参数时,我不是100%肯定我可能遇到的关于 de 序列化的障碍。我假设DataContractJsonSerializer也将用于此。当我来到它时会穿过那座桥......如果它造成了问题。到目前为止,还没有我的简单DTO。
<强>更新强>
请参阅Oleg的答案(UPDATE2部分)。他将服务方法的返回类型从void更改为System.ServiceModel.Channels.Message
,而不是使用HttpContext.Current.Response.Write()
,他使用:
return WebOperationContext.Current.CreateTextResponse (json,
"application/json; charset=utf-8", Encoding.UTF8);
这确实是一个更好的解决方案。谢谢Oleg。
更新2 还有另一种方法可以实现这一点。将服务的返回类型从Message更改为Stream,并返回:
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json));
我还没有做过任何特定的测试,但对于可能会返回大量数据的方法,这可能是更好的选择。我不知道这对非二进制数据是否重要。无论如何,一个想法。
答案 1 :(得分:11)
在我看来,你使用不正确DataContractJsonSerializer
。奇怪的是:您没有为ResponseFormat = ResponseFormat.Json
方法定义public SimpleMessage SayHelloObject()
属性。
此外,如果您在字符串中有{"Message":"Hello World"}
并在调试器中显示它,它将显示为"{\"Message\":\"Hello World\"}"
,所以就像您看到string json = JsonConvert.Serialize(message);
(Json.Net)一样。所以在我看来,你在两种情况下都有相同的结果。
要验证这一点,请使用读取结果的客户端软件。看一些例子
JQuery ajax call to httpget webmethod (c#) not working
Can I return JSON from an .asmx Web Service if the ContentType is not JSON?
How do I build a JSON object to send to an AJAX WebService?
更新:在您的代码中定义方法SayHelloString()
。它的结果是一个字符串。如果您调用该方法,此字符串将再次 JSON序列化。字符串{"Message":"Hello World"}
的JSON序列化是带引号的字符串(请参阅http://www.json.org/定义,不是对象,而是字符串)或字符串"{\"Message\":\"Hello World\"}"
。所以Web服务的两种方法都是正确的。
更新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);
}
您可以使用另一个Message 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
?是性能提升?您是否需要以DateTime
的其他方式实现某些数据类型(如DataContractJsonSerializer
)的序列化?或者您选择Json.Net
的主要原因还有其他原因吗?