【发布时间】:2012-12-11 21:30:22
【问题描述】:
我将从我的问题开始:
我有一个通过 AJAX/JSON 调用的网络方法。 我们称它为“GlobalMethod”,用于管理“容器”对象,该对象具有从同一“基类”派生的项目列表。 这是我的情况的代码示例:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class MyService : System.Web.Services.WebService
{
[WebMethod]
public string GlobalMethod(Container data)
{
string result = "";
foreach (var item in data.Items)
{
result += item.BaseProperty;
if (item is FirstDerivedClass)
result += ((FirstDerivedClass)item).FirstDerivedProperty;
else if (item is SecondDerivedClass)
result += ((SecondDerivedClass)item).SecondDerivedProperty;
}
return result;
}
}
public class Container
{
public List<BaseClass> Items;
}
public abstract class BaseClass
{
public string BaseProperty;
}
public class FirstDerivedClass : BaseClass
{
public string FirstDerivedProperty;
}
public class SecondDerivedClass : BaseClass
{
public string SecondDerivedProperty;
}
这种方法根本行不通。我将无法使用默认的 JavascriptSerializer 调用此方法,因为序列化程序无法解析容器将拥有的对象类型:它们可能是 FirstDerivedClass 或 SecondDerivedClass 类型。
所以,浏览网页寻找我的问题的解决方案,我遇到了Json.NET,其方法 JsonConvert.DeserializeObject 能够检索使用 JsonConvert.SerializeObject 序列化的对象的原始类型,因为它添加了一个名为“$type”的属性。
我现在的问题是:如何使用此序列化程序而不是 ASMX 使用的默认序列化程序来制作 webmethod? 如果方法签名仍然存在
[WebMethod]
public string GlobalMethod(Container data)
然后我会得到一个完全为空的 Container 对象,因为框架正在执行反序列化工作并且不知道要实例化哪些项目,而且我无法告诉框架它应该使用 Json.NET 来完成这项工作,这将能够完全反序列化数据。
如果我尝试以这种方式修改方法
[WebMethod]
public string GlobalMethod(string data)
{
string result = "";
Container container = JsonConvert.DeserializeObject<Container>(data,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
foreach (var item in container.Items) {
...
然后我会收到另一个服务器错误 500,因为 “没有为 u0027system.string u0027 类型定义无参数构造函数”
好的,希望我的问题很清楚......我已经花了一整天的时间来解决这个问题,但我似乎没有找到解决方案。重新审视我的方法的体系结构以避免使用派生类并不是一个很好的选择(注意实际的代码要复杂得多,我只是简化了它以达到重点!)。
顺便补充一句,调用webmethod的ajax方法是这样完成的:
$.ajax({
type: "POST",
url: wsUrl + method,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json",
processData: false,
success: function(msg)
{
try{
success(JSON.parse(msg.d));
} finally {
}
},
error: error
});
感谢任何愿意与我分享他的知识的人!
我想分享一下我是如何实际解决我的问题的。正如我已经说过的,我这样修改了我的方法:
[WebMethod]
public string GlobalMethod(string data)
{
string result = "";
Container container = JsonConvert.DeserializeObject<Container>(data,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
foreach (var item in container.Items) {
最初,我开始收到错误 500 "no parameterless constructor defined for type of u0027system.string u0027"
但最终我发现了原因:从客户端传输到服务器的 json 字符串被 .NET 框架反序列化为包含的对象,它不是字符串,而是实际的 JSON 对象!
所以诀窍如下,在我的 ajax 方法中:
$.ajax({
type: "POST",
url: wsUrl + method,
**data: JSON.stringify(JSON.stringify(data)),**
contentType: "application/json; charset=utf-8",
dataType: "json",
processData: false,
success: function(msg)
{
try{
success(JSON.parse(msg.d));
} finally {
}
},
error: error
});
这意味着json对象被封装在另一个字符串中,将由我的服务器端GlobalMethod(string)通过Json.NET手动反序列化。
显然我不会在 ajax 例程中包含这个双重“字符串化”,但我会注意在输入中将 JSON.stringified 数据传递给 ajax 例程本身!
【问题讨论】:
-
你考虑过使用 WCF 吗?您可以更好地控制消息的序列化和反序列化方式。
-
这已经被考虑过了,但暂时避免了,因为与我们架构的其余部分保持一致。如果它是唯一可用的解决方案,我们只会这样做。无论如何,谢谢,您认为使用 WCF 我们能够反序列化我们的对象吗?有什么特别的配置吗?
-
我在我的网站上使用 WCF 中的 json.net 序列化程序,我关注 this article 让我开始。我还在我的博客上写了this post 和我的一些学习。据我所知,没有办法用 ASP.NET Web 服务覆盖 JSON 格式,除非自己重新编写整个处理程序。
-
感谢迈克,非常有用,虽然不是真正解决我的问题。但下次我们肯定会使用 WCF,因为它们的可定制性要高得多
标签: json asmx json.net deserialization webmethod