从JavaScript数组创建接口类型列表

时间:2013-09-10 17:59:47

标签: c# javascript jquery

假设有一个简单的界面:

public interface ISerialize
    {
        string FirstProp { get; set; }
        string SecondProp { get; set; }

    }

由类实现:

   public class Class1 : ISerialize
   {
        public string FirstProp { get; set; }
        public string SecondProp { get; set; }
        public string ThirdProp { get; set; }
   }
    public class Class2 : ISerialize
    {
         public string FirstProp { get; set; }
         public string SecondProp { get; set; }
         public string FourthProp { get; set; }
    }

目前(这不是为了长期稳定)我有一个网页,看起来像: http://jsfiddle.net/SBbPT/其中每个文本框对应于Class1或Class2对象中的属性,Add to batch链接将对象添加到JavaScript数组中,Submit batch按钮将JSON字符串发送到Web服务字符串化的对象。 目前,以下JS确定创建了Class1Class2的类型:

 $(document).ready(function ()
        {
            var iSerialize = [];
            $('#btnSubmit').click(function ()
            {
                //creates Class1 object if ThirdProp is present
                if ($('#txt3').val().length > 0)
                {
                    var class1 = { FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), ThirdProp: $('#txt3').val() }
                    iSerialize.push(class1);
                }
                else
                {
                    var class2 = { FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), FourthProp: $('#txt4').val() };
                    iSerialize.push(class2);
                }
                $('input').val('');
            });
            $('#btnSubmitBatch').click(function ()
            {
                var data = "{jsonString:'" + JSON.stringify(iSerialize) + "'}";
                console.log(data);
                $.ajax(
                {
                    type: "POST",
                    url: "default.aspx/DataList",
                    contentType: "application/json",
                    dataType: "json",
                    data: data,
                    success: function (data)
                    {
                        console.log('the post was successful');
                        console.log(data.d);
                    },
                    error: function (xhr)
                    {
                        console.log(xhr.status);
                    }
                });
            });

        });

目前,如果用户将FourthProp文本框留空,则应创建Class1对象,如果用户将ThirdProp文本框留空,则应创建Class2对象。我当前的Web服务方法如下:

[WebMethod]
        public string DataList(string jsonString)
        {
            var jss = new JavaScriptSerializer();
            List<ISerialize> list = jss.Deserialize<List<ISerialize>>(jsonString);
            //some members might have different properties
            //how to create a factory to create an instance based on the objects properties?
            return list[0].FirstProp;
        }

在当前状态下,我收到一个错误:No parameterless constructor defined for type of DeserializeListOfInterfaceTypes.ISerialize.这可以避免,程序将通过使List<ISerialize>列出其中一个具体类型来工作。因此,在这种情况下,属性ThirdPropFourthProp的存在将分别确定对象应该是Class1还是Class2。如何使用JavaScript对象的属性来确定要创建的C#对象?

4 个答案:

答案 0 :(得分:2)

问题在于,反序列化器必须实例化类以填充列表,而不是接口。但它无法知道哪个班级。

所以你的问题归结为:如何提示反序列化器为每个JSON对象创建哪个类?

在JSON中没有标准的方法来包含“类”描述(至少目前为止)。您必须提出自己的规则(包括'type'属性,或者像您建议的那样探测现有属性)。当然,JSON框架无法知道规则,所以它不会“神奇地”发生。

假设你使用Newtonsoft的JSON.net进行序列化(如果你没有 - 你应该)。这个问题在这里得到了回答:

How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?

它基本上归结为覆盖JsonConverter

答案 1 :(得分:2)

您可以通过两种方式解决此问题:

  1. 编写一个自定义序列化程序,它将了解您的具体要求,并具有关于要实例化的确切类型的逻辑。
  2. 根据Web应用程序的语义更改服务器的对象模型。
  3. 如果采用方法1而不是JavaScriptSerializer,则应实施自定义方法。以下是如何执行此操作的起点:http://msdn.microsoft.com/en-us/library/ty01x675.aspx

    如果您决定更改对象模型,则需要确保它可以使用默认的JavaScriptSerializer,它需要特定类型来实例化。现在,根据语义,Class1Class2可能彼此相似(继承)。在这种情况下,您可以将当前包含在接口中的属性提取到基类中,并使任何需要重写为虚拟的内容。然后Class1Class2将覆盖属性,但它们仍将在基类中定义,并且序列化程序将能够创建该类的实例。您应该将基类作为序列化程序的类型,而不是接口。如果Class1Class2不共享任何行为,您仍然可以创建一个仅用于序列化的基类。当然,这取决于语义,我对此一无所知。

答案 2 :(得分:1)

我个人选择的路线可能如下:

创建一个类,可以托管Class1Class2的任何给定输入,例如

public class ClassInput
{
    public string FirstProp { get; set; }
    public string SecondProp { get; set; }
    public string ThirdProp { get; set; }
    public string FourthProp { get; set; }
}

使用此类作为Web方法的参数。

然后在服务器端确定应该创建哪个类。

为每个Class1Class2创建一个方法,创建该类并映射ClassInput中的每个属性。

public Class1 CreateClass1(ClassInput input)
{
    Class1 output = new Class1();
    output.FirstProp = input.FirstProp;
    output.SecondProp = input.SecondProp;
    output.ThirdProp = input.ThirdProp;
    return output;
}

public Class2 CreateClass2(ClassInput input)
{
    Class2 output = new Class1();
    output.FirstProp = input.FirstProp;
    output.SecondProp = input.SecondProp;
    output.FourthProp = input.FourthProp;
    return output;
}

如果您有许多类/属性,使用AutoMapper可以在这里保存一些代码。

答案 3 :(得分:1)

如果您想继续使用JavaScriptSerializer,那么您可以撰写自定义JavaScriptTypeResolver

public class CustomTypeResolver : JavaScriptTypeResolver
{
    public override Type ResolveType(string id)
    {
        return id == "class1" ? typeof(Class1) : typeof(Class2);
    }

    public override string ResolveTypeId(Type type)
    {
        return type == typeof(Class1) ? "class1" : "class2";
    }
}

然后按如下方式构建序列化程序:

var jss = new JavaScriptSerializer(new CustomTypeResolver());

最后,您需要将此类型信息添加到JavaScript代码中的__type字段:

$('#btnSubmit').click(function ()
{
    //creates Class1 object if ThirdProp is present
    if ($('#txt3').val().length > 0)
    {
        var class1 = { __type: "class1", FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), ThirdProp: $('#txt3').val() }
        iSerialize.push(class1);
    }
    else
    {
        var class2 = { __type: "class2", FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), FourthProp: $('#txt4').val() };
        iSerialize.push(class2);
    }
    $('input').val('');
});

但是,如果您不想向对象添加任何其他属性,那么我同意jods's answer并使用Newtonsoft's JSON.NET代替JavaScriptSerializer