我可以使用DataContract序列化程序序列化Dictionary <string,object>吗?

tis*_*hma 9 wcf serialization json datacontractserializer

我打算构建一个WCF服务,返回序列化为JSON的通用字典对象.不幸的是,序列化失败,因为对象可能总是不同.KnownTypes无法帮助,因为属性类型是Dictionary,我不能说KnownType,因为类可能总是不同.

如果有可能序列化"未知类型"的任何想法?

我不介意为每个类指定DataContract/DataMember,但(至少对于原型版本)我不希望每个响应都有强类型.Javascript客户端只是不在乎.

匿名课怎么样?

the*_*onk 7

注意:我在答案开始时已经了解了很多关于JavaScriptSerializer的细节,如果您只想阅读原始问题中提到的已知类型问题的解决方案,请跳到答案的末尾.

性能

基于我运行的基准测试,JavaScriptSerializer比其他替代方案慢得多,并且与DataContractSerializer相比,可以花费2倍的时间来序列化/反序列化对象.

不需要已知类型

也就是说,JavascriptSerializer更灵活,因为它不需要您提前指定"已知类型",并且序列化的JSON至少在字典的情况下更清晰(参见此处的示例).

围绕已知类型的灵活性的另一面是它无法将相同的JSON字符串反序列化回原始类型.例如,假设我有一个简单的Person类:

public class Person
{
    public string Name { get; set; }

    public int Age { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果我在序列化之前创建了一个实例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);
Run Code Online (Sandbox Code Playgroud)

我将获得以下JSON {"me":{"Name":"Yan","Age":30}},它非常干净,但没有任何类型信息.所以假设你有两个具有相同成员定义的类,或者如果Person是子类而不引入任何其他成员:

public class Employee : Person
{
}
Run Code Online (Sandbox Code Playgroud)

那么序列化器根本无法保证JSON {"Name":"Yan","Age":30}可以反序列化为正确的类型.

如果{"me":{"Name":"Yan","Age":30}}使用JavaScriptSerializer进行反序列化,则在字典中返回与"me"相关联的值不是一个实例,Person而是Dictionary<string, object>一个简单的属性包.

如果你想获得一个Person实例,你可以(虽然你很可能永远不会想!)Dictionary<string, object>使用ConvertToTypehelper方法转换它:

var clone = serializer.Deserialize<Dictionary<string, object>>(json);
var personClone = serializer.ConverToType<Person>(clone["me"]);
Run Code Online (Sandbox Code Playgroud)

在另一方面,如果你并不需要担心反序列化JSON那些为正确的类型和JSON serailization不是一个性能瓶颈(分析代码并找出多少CPU时间的花费上的序列化,如果你还没有这样做已经)然后我会说只使用JavaScriptSerializer.

注入已知类型

如果,在一天结束时,你仍然需要使用DataContractSerializer并需要注入那些KnownTypes,这里有两件事你可以试试.

1)将已知类型的数组传递给DataContractSerializer 构造函数.

2)将DataContractResolver的子类(带有定位您感兴趣的类型的方法)传递给DataContractSerializer 构造函数

您可以创建各种各样的"已知类型的注册表",用于跟踪,可被添加到字典的类型,如果你控制,你需要注入到DataContractSerializer的所有类型,你可以尝试简单的事情:

  1. 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();
        }
    }
  2. 添加一个静态构造函数,用寄存器注册类型:

    [DataContract]
    public class Person
    {
        static Person()
        {
            KnownTypeRegister.Add(typeof(Person));
        }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public int Age { get; set; }
    }
  3. 在构造序列化程序时从寄存器中获取已知类型的数组:

var serializer = new DataContractSerializer(typeof(Dictionary<string, object>), KnownTypeRegister.Get());

更多动态/更好的选择是可能的,但他们也更难以实现,如果你想了解更多有关动态已知类型解析,看看在话题朱瓦尔·洛的MSDN文章在这里.此外,Carlos Figueira撰写的这篇博文还详细介绍了更多先进的技术,例如动态生成类型,非常值得您阅读的主题!