【问题标题】:Create list of interface types from JavaScript array从 JavaScript 数组创建接口类型列表
【发布时间】:2013-09-14 13:46:31
【问题描述】:

假设有一个简单的界面:

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判断创建Class1还是Class2的类型:

 $(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&lt;ISerialize&gt; 成为具体类型之一的列表来工作。所以在这种情况下,属性ThirdPropFourthProp 的存在决定了对象应该分别是Class1 还是Class2。如何使用 JavaScript 对象的属性来确定要创建的 C# 对象?

【问题讨论】:

    标签: c# javascript jquery


    【解决方案1】:

    问题是反序列化器必须实例化类来填充您的列表,而不是接口。但它无法知道哪个类。

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

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

    假设您使用 Newtonsoft 的 JSON.net 进行序列化(如果您不使用 - 您应该这样做)。在这里提出并回答了这个确切的问题:

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

    它基本上归结为覆盖JsonConverter

    【讨论】:

      【解决方案2】:

      你可以通过两种方式解决这个问题:

      1. 编写一个自定义序列化程序,它会了解您的具体要求,并对要实例化的确切类型有逻辑。
      2. 根据 Web 应用程序的语义更改服务器的对象模型。

      如果您采用方法 1,而不是 JavaScriptSerializer,您应该实现自定义方法。以下是如何做到这一点的起点:http://msdn.microsoft.com/en-us/library/ty01x675.aspx

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

      【讨论】:

        【解决方案3】:

        如果你想继续使用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

        【讨论】:

          【解决方案4】:

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

          创建一个可以承载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 可以节省一些代码。

          【讨论】:

            猜你喜欢
            • 2022-07-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-02-06
            相关资源
            最近更新 更多