从 JavaScript 数组创建接口类型列表
本文关键字:接口类型 列表 创建 数组 JavaScript | 更新日期: 2023-09-27 18:30:54
假设有一个简单的界面:
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);
}
});
});
});
目前,如果用户将 FourProp 文本框留空,则应创建一个 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>
成为特定类型之一的列表来工作。 因此,在这种情况下,属性ThirdProp
或FourthProp
的存在决定了对象是应该Class1
还是Class2
,分别。 如何使用 JavaScript 对象的属性来确定要创建的 C# 对象?
问题是反序列化程序必须实例化类来填充列表,而不是接口。但它无法知道哪个阶级。
所以你的问题归结为:如何提示反序列化程序为每个 JSON 对象创建哪个类?
没有在JSON中包含"类"描述的标准方法(至少目前是这样)。您必须提出自己的规则(包括"类型"属性,或像您建议的那样探测现有属性)。当然,JSON框架无法知道规则,所以它不会"神奇地"发生。
假设您使用Newtonsoft的 JSON.net 进行序列化(如果您不这样做 - 您应该这样做)。这里提出并回答了这个确切的问题:
如何在 JSON.NET 中实现自定义 JsonConverter 以反序列化基类对象列表?
它基本上归结为覆盖JsonConverter
。
您可以通过两种方式解决此问题:
- 编写一个自定义序列化程序,它将知道你的特定要求,并具有有关要实例化的确切类型的逻辑。
- 根据 Web 应用程序的语义更改服务器的对象模型。
如果您采用方法 1,而不是 JavaScriptSerializer
,则应实现自定义方法。这是如何做到这一点的起点:http://msdn.microsoft.com/en-us/library/ty01x675.aspx
如果决定更改对象模型,则需要确保它与默认 JavaScriptSerializer
一起使用,该 需要实例化特定类型。现在,根据语义,Class1
和Class2
可能彼此相似(继承)。在这种情况下,您可以将当前包含在接口中的属性提取到基类中,并使需要覆盖的任何内容成为虚拟属性。然后,Class1
和 Class2
将重写这些属性,但它们仍将在基类中定义,并且序列化程序将能够创建该类的实例。应将基类作为类型提供给序列化程序,而不是接口。如果Class1
和Class2
不共享任何行为,您仍然可以创建一个基类以仅用于序列化。当然,这取决于语义,对此我一无所知。
如果你想继续使用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 的答案并使用 Newtonsoft 的 JSON.NET 而不是 JavaScriptSerializer
。
我个人选择的路线可能如下:
创建一个类,该类可以承载任何给定的输入,用于Class1
加Class2
例如
public class ClassInput
{
public string FirstProp { get; set; }
public string SecondProp { get; set; }
public string ThirdProp { get; set; }
public string FourthProp { get; set; }
}
使用此类作为 Web 方法的参数。
然后在服务器端决定应该创建哪个类。
为Class1
和Class2
中的每一个都有一个方法,该方法创建该类并从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;
}
如果您有许多类/属性,使用自动映射器可以在此处节省一些代码。