4

我将从我的问题开始:

我有一个通过 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 序列化的对象的原始类型,因为它添加了一个名为“ $类型”。

我现在的问题是:如何使用此序列化程序而不是 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 “没有为 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 例程中包含这个双重“stringify”,但我会注意将输入中的 JSON.stringified 数据传递给 ajax 例程本身!

4

2 回答 2

3

这只是一个技巧,只是一个想法,听起来并不优雅,但它应该可以工作我只是好奇

以这种方式更改容器:

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;

}

Ps:这很粗糙,应该改进。这有意义吗?

于 2012-12-12T14:30:54.217 回答
2

更改 web 方法以接受object

[WebMethod]
public string GlobalMethod(object data)

这应该可以解决您遇到的问题。

于 2012-12-11T21:34:48.073 回答