首页 文章

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

提问于
浏览
9

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

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

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

匿名课怎么样?

2 回答

  • 1

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

    Performance

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

    No need for Known Type

    也就是说,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 serailization不是性能瓶颈(如果您还没有这样做,请查看代码并了解在序列化上花费了多少CPU时间)已经)然后我会说只使用JavaScriptSerializer .

    Injecting Known Type

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

    1)将已知类型的数组传递给DataContractSerializer constructor .

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

    您可以创建一个“已知类型注册表”,它们可以跟踪可以添加到字典中的类型,如果您控制了需要注入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());

    更多动态/更好的选项是可能的,但他们're also more difficult to implement, if you want to read more about dynamic known type resolution, have a look at Juval Lowy'的MSDN文章主题here . 此外,Carlos Figueira的this blog post也详细介绍了更多先进技术,例如动态生成类型,非常值得您阅读主题!

  • 7

    disclaimer 我不建议你在WCF endpoints 上公开 object ;虽然这似乎是'flexible'这不是一个好主意,因为你没有指定你的服务将提供什么样的信息 .

    现在回答

    如果你的WCF调用正在被ajax调用消耗,并且你把它放到 Javascript client just doesn't care. 那么为什么不让你的WCF调用只返回一个字符串?然后,WCF调用的内部可以使用JavaScriptSerializer序列化 Dictionary<string, object>

    public string MyServiceMethod()
    {
        var hash = new Dictionary<string, object>();
        hash.Add("key1", "val1");
        hash.Add("key2", new { Field1 = "field1", Field2 = 42});
        var serialized = new JavaScriptSerializer().Serialize(hash);
        return serialized;
    }
    

    disclaimer2 这个是一种达到目的的手段(因为你问了这个问题) - 对于一个 生产环境 质量的应用程序,我会有一个定义良好的界面,因此很明显是什么被请求并通过网络发回 .

相关问题