注意:如果您只是想了解原始问题中提到的已知类型问题的解决方案,我在回答开始时已经详细介绍了 JavaScriptSerializer,跳到答案的末尾。
性能
基于我运行的benchmarks,JavaScriptSerializer 比其他替代方案慢得多,与 DataContractSerializer 相比,序列化/反序列化对象所需的时间是 DataContractSerializer 的 2 倍。
不需要已知类型
也就是说,JavascriptSerializer 更灵活,因为它不需要您提前指定“已知类型”,并且序列化的 JSON 至少在字典的情况下更清晰(参见示例here)。
这种围绕已知类型的灵活性的另一面是它无法将相同的 JSON 字符串反序列化回原始类型。例如,假设我有一个简单的Person 类:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
如果我创建一个Dictinoary<string, object> 的实例并在序列化它之前添加一个Person 类的实例:
var dictionary = new Dictionary<string, object>();
dictionary.Add("me", new Person { Name = "Yan", Age = 30 });
var serializer = new new JavaScriptSerializer();
var json = serializer .Serialize(dictionary);
我将得到以下 JSON {"me":{"Name":"Yan","Age":30}},它非常干净但没有任何类型信息。所以假设如果您有两个具有相同成员定义的类,或者如果 Person 被子类化而不引入任何其他成员:
public class Employee : Person
{
}
那么序列化程序根本无法保证 JSON {"Name":"Yan","Age":30} 可以反序列化为正确的类型。
如果您使用 JavaScriptSerializer 反序列化 {"me":{"Name":"Yan","Age":30}},则在字典中您会返回与“me”关联的值不是 Person 的实例,而是 Dictionary<string, object>,而是一个简单的属性包。
如果您想取回 Person 实例,您可以(尽管您很可能永远不会想要!)使用 ConvertToType 辅助方法转换该 Dictionary<string, object>:
var clone = serializer.Deserialize<Dictionary<string, object>>(json);
var personClone = serializer.ConverToType<Person>(clone["me"]);
另一方面,如果您不需要担心将这些 JSON 反序列化为正确的类型,并且 JSON 序列化不是性能瓶颈(分析您的代码并找出在序列化上花费了多少 CPU 时间(如果没有的话) t 已经这样做了)然后我会说只使用 JavaScriptSerializer。
注入已知类型
如果,归根结底,您仍然需要使用 DataContractSerializer 并需要注入那些 KnownTypes,您可以尝试以下两件事。
1) 将已知类型的数组传递给 DataContractSerializer constructor。
2) 将DataContractResolver 的子类(用于定位您感兴趣的类型)传递给DataContractSerializer constructor
您可以创建一个“已知类型注册表”来跟踪可以添加到字典中的类型,如果您控制需要注入 DataContractSerializer 的所有类型,您可以尝试最简单的事情:
-
使用静态方法创建一个KnownTypeRegister 类以将类型添加到已知类型列表中:
公共静态类 KnownTypeRegister
{
私有静态只读 ConcurrentBag _knownTypes = new ConcurrentBag();
公共静态无效添加(类型类型)
{
_knownTypes.Add(type);
}
公共静态 IEnumerable Get()
{
返回 _knownTypes.ToArray();
}
}
-
添加一个静态构造函数,用寄存器注册类型:
[数据合约]
公共类人
{
静态人()
{
KnownTypeRegister.Add(typeof(Person));
}
[数据成员]
公共字符串名称 { 获取;放; }
[数据成员]
公共 int 年龄 { 得到;放; }
}
在构造序列化器时从寄存器中获取已知类型的数组:
var serializer = new DataContractSerializer(typeof(Dictionary<string, object>), KnownTypeRegister.Get());
更多动态/更好的选项是可能的,但它们也更难以实现,如果您想了解更多关于动态已知类型解析的信息,请查看 Juval Lowy 关于主题 here 的 MSDN 文章。
此外,Carlos Figueira 的 this blog post 还详细介绍了更高级的技术,例如动态生成类型,非常值得一读!