如何使用 Json.Net 从我的 WCF 休息服务(.NET 4)返回 json,而不是用引号括起来的字符串?
Posted
技术标签:
【中文标题】如何使用 Json.Net 从我的 WCF 休息服务(.NET 4)返回 json,而不是用引号括起来的字符串?【英文标题】:How can I return json from my WCF rest service (.NET 4), using Json.Net, without it being a string, wrapped in quotes? 【发布时间】:2011-03-02 21:36:11 【问题描述】:2010 年 10 月 19 日更新 我知道我不久前问过这个问题,但是这些答案中显示的解决方法很难令人满意,这仍然是许多人的常见问题。 WCF 只是不灵活。我创建了自己的开源 C# 库,用于在没有 WCF 的情况下创建 REST 服务。检查restcake.net 或rest.codeplex.com 以获取有关该库的信息。 结束更新
2012 年 8 月 2 日更新 ASP.NET Web API(以前的 WCF Web API,REST WCF 的替代品)默认使用 Json.NET 结束更新
DataContractJsonSerializer
无法处理 Json.Net 在正确配置(特别是循环)时处理得很好的许多场景。
服务方法可以返回一个特定的对象类型(在这种情况下为DTO),在这种情况下将使用DataContractJsonSerializer
,或者我可以让该方法返回一个字符串,然后自己进行序列化Json.Net。问题是,当我返回一个 json 字符串而不是一个对象时,发送给客户端的 json 用引号括起来。
使用DataContractJsonSerializer
,返回具体的对象类型,响应为:"Message":"Hello World"
使用Json.Net返回一个json字符串,响应为:"\"Message\":\"Hello World\""
我不想在客户端上对结果进行 eval() 或 JSON.parse(),如果 json 作为字符串返回并用引号括起来,我必须这样做。我意识到行为是正确的;这不是我想要/需要的。我需要原始的json;服务方法的返回类型是对象而不是字符串时的行为。
那么,我怎样才能让我的方法返回一个对象类型,但不使用 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.
【问题讨论】:
我不确定我理解您为什么要使用任何序列化。如果您希望在 WCF 服务中以 JSON 形式返回对象,您只需在合同中将响应格式设置为WebMessageFormat.Json
。
Cody DataContract 糟透了。你不能做关键值,而且日期被搞砸了,没有回报。它也较慢,但这并不重要
【参考方案1】:
我终于找到了解决办法。这不是我想要的(返回特定的对象类型,并以某种方式指示 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 托管应该与主机无关。
使用这种方法,在 firebug 中 GET 请求的结果看起来很完美。正确的内容类型、正确的内容长度和原始 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);
这确实是一个更好的解决方案。谢谢奥列格。
更新 2 还有另一种方法可以做到这一点。将服务的返回类型从 Message 更改为 Stream,并返回:
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json));
我没有做过任何具体的测试,但是对于可能返回大量数据的方法来说,这可能是一个更好的选择。我不知道这对非二进制数据是否重要。无论如何,一个想法。
【讨论】:
【参考方案2】:在我看来,您使用的DataContractJsonSerializer
不正确。奇怪的是:你没有为public SimpleMessage SayHelloObject()
方法定义ResponseFormat = ResponseFormat.Json
属性。
此外,如果您在字符串中有"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);
您当然可以使用另一个消息格式化程序:例如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
那样以其他方式实现某些数据类型(如DateTime
)的序列化?还是你选择Json.Net
的主要原因是别的?
【讨论】:
在我的 web.config 中,我的"Message":"Hello World"
是有效的JSON 数据,但"\"Message\":\"Hello World\""
不是。尝试粘贴您在 Fiddler/FireBug 中看到的 jsonlint.com 确切字符串,您将看到您是否有正确的 JSON。在json.org,您将找到 JSON 规范。
我意识到 "\"Message\":\"Hello World\"" 不是有效的“原始”json。它是有效的 json,作为字符串传递,所以用引号括起来。如果将它传递给 eval() 或 JSON.parse(),那么它的评估就很好,因为它 is 是有效的 json。但是,这既不是我的问题,也不是我的问题。
当你写“我可以自己返回原始 json?没有包装引号?”时,你是什么意思?你想大概返回Message:"Hello World"
吗?但是 JSON 是错误的(试试jsonlint.com)。您计划如何在客户端使用数据?如果您持有标准,则可以使用标准序列化程序。如果你想使用紧凑的数据,你可以使用二进制序列化器。如果您只想记录您的对象,您可以枚举所有对象属性并在那里写入。但如果您使用“JSON”作为格式名称,您必须遵守 JSON 规范 json.org。
好的。有趣的!关于包含循环的图的序列化信息对我来说是新的。我没有这个结构,但可能我应该更仔细地查看 Json.Net。顺便说一句,Massage
类型的使用也可以帮助 Web 方法的输入参数。以上是关于如何使用 Json.Net 从我的 WCF 休息服务(.NET 4)返回 json,而不是用引号括起来的字符串?的主要内容,如果未能解决你的问题,请参考以下文章