NOTE:我在答案开始时详细介绍了 JavaScriptSerializer,如果您只想阅读原始问题中提到的已知类型问题的解决方案,请跳到答案的末尾。
表现
基于基准 http://theburningmonk.com/benchmarks/我跑了一下,JavaScriptSerializer 比其他替代方案慢得多,并且与 DataContractSerializer 相比,序列化/反序列化对象所需的时间是 DataContractSerializer 的 2 倍。
不需要已知类型
也就是说,JavascriptSerializer 更灵活,因为它不需要您提前指定“已知类型”,并且序列化的 JSON 至少在字典的情况下更干净(请参阅示例here http://theburningmonk.com/2011/05/json-serialization-datacontractjsonserializer-vs-javascriptserializer/).
围绕已知类型的灵活性的另一面是,它无法将相同的 JSON 字符串反序列化回原始类型。例如,假设我有一个简单的Person
class:
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}
可以反序列化为正确的类型。
如果你反序列化{"me":{"Name":"Yan","Age":30}}
使用 JavaScriptSerializer,在字典中你得到的与“me”相关的值不是一个实例Person
but a Dictionary<string, object>
取而代之的是一个简单的财产袋。
如果你想获得一个Person
回到实例,你可以(尽管你很可能永远不想!)转换它Dictionary<string, object>
使用ConvertToType
辅助方法:
var clone = serializer.Deserialize<Dictionary<string, object>>(json);
var personClone = serializer.ConverToType<Person>(clone["me"]);
另一方面,如果您不需要担心将这些 JSON 反序列化为正确的类型,并且 JSON 序列化不是性能瓶颈(分析您的代码并找出序列化花费了多少 CPU 时间,如果您还没有这样做的话)已经)那么我会说只使用 JavaScriptSerializer。
注入已知类型
如果最终您仍然需要使用 DataContractSerializer 并需要注入这些 KnownType,那么您可以尝试以下两件事。
1)将已知类型的数组传递给DataContractSerializer构造函数 http://msdn.microsoft.com/en-us/library/bb908209.aspx.
2)传递一个子类数据契约解析器 http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractresolver.aspx(能够找到您感兴趣的类型)到 DataContractSerializer构造函数 http://msdn.microsoft.com/en-us/library/bb908209.aspx
您可以创建一个“已知类型注册表”来跟踪可以添加到字典中的类型,如果您控制需要注入 DataContractSerializer 的所有类型,您可以尝试最简单的方法:
-
创建一个KnownTypeRegister
具有静态方法的类将类型添加到已知类型列表:
public static class KnownTypeRegister
{
private static readonly ConcurrentBag _knownTypes = new ConcurrentBag();
public static void Add(Type type)
{
_knownTypes.Add(type);
}
public static IEnumerable Get()
{
return _knownTypes.ToArray();
}
}
-
添加一个静态构造函数,用寄存器注册类型:
[DataContract]
public class Person
{
static Person()
{
KnownTypeRegister.Add(typeof(Person));
}
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
构造序列化器时从寄存器获取已知类型的数组:
var serializer = new DataContractSerializer(typeof(Dictionary<string, object>), KnownTypeRegister.Get());
更多动态/更好的选项是可能的,但它们也更难实现,如果您想了解有关动态已知类型解析的更多信息,请查看 Juval Lowy 的 MSDN 主题文章here http://msdn.microsoft.com/en-us/magazine/gg598929.aspx.
Also, 这篇博文 http://blogs.msdn.com/b/carlosfigueira/archive/2011/09/21/wcf-extensibility-data-contract-resolver.aspxCarlos Figueira 还详细介绍了更先进的技术,例如动态生成类型,在您讨论该主题时非常值得一读!