我将从我的问题开始:
我有一个通过AJAX/JSON调用的Web方法。让我们称之为"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或SecondDerivedCClass类型。
因此,在浏览web以寻找问题的解决方案时,我遇到了Json.NET,它的方法JsonConvert.DeserializeObject能够检索使用JsonConvert.SerializeObject序列化的原始类型的对象,因为它添加了一个名为"$type"的属性。
我现在的问题是:如何使用这个序列化程序而不是ASMX使用的默认序列化程序来制作Web方法?如果方法签名保持
[WebMethod]
public string GlobalMethod(Container data)
然后,我将得到一个完全空的容器对象,因为框架正在进行反序列化工作,不知道要实例化哪些项,而且我无法告诉框架它应该使用Json.NET来执行该工作,因为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类型定义无参数构造函数">
好的,希望我的问题很清楚。。。我花了一整天的时间在这上面,似乎没有找到解决方案。为了避免使用派生类,重新修改我的方法的体系结构并不是一个很好的选择(注意实际的代码要复杂得多,我只是简化了它来切中要害!)。
顺便说一句,我将补充一下,调用web方法的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"没有为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(字符串)通过json.NET.手动反序列化
很明显,我不会在ajax例程中包含这个双"字符串",但我会注意将输入中的JSON.stringized数据传递给ajax例程本身!
这只是一个破解,这只是一个想法,听起来不太优雅,但应该有效我只是好奇
以这种方式更改容器:
public class Container
{
public List<MyUnionBaseClass> Items;
}
并定义(伪代码):
public class MyUnionBaseClass{
public void MyUnionBaseClass(BaseClass b){
this.setValue(b);
};
final public BaseClass getValue(){
if(first!=null) return (BaseClass) first;
if(second!=null)return (BaseClass) second;
return null;
}
final public setValue(BaseClass b){
if(b instanceOf firstClass){
first = (FirstClass) b;
}
if(b instanceOf secondClass){
second = (SecondClass) b;
}
}
private FirstDerivedClass first=null;
private SecondDerivedClass second=null;
}
附言:这太粗糙了,应该改进一下。这有道理吗?
更改Web方法以接受object
[WebMethod]
public string GlobalMethod(object data)
这应该可以解决您遇到的问题。