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

时间:2012-04-26 11:06:23

标签: json wcf serialization datacontractserializer

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

是否有可能序列化“未知类型”?

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

匿名课怎么样?

2 个答案:

答案 0 :(得分:7)

注意:如果您只是想了解原始问题中提到的已知类型问题的解决方案,我在答案开始时已经了解了很多关于JavaScriptSerializer的详细信息,跳到答案的最后。

<强>性能

基于我运行的benchmarks,与DataContractSerializer相比,JavaScriptSerializer远远慢于其他替代品,并且可以花费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}},则在字典中返回与“我”相关联的值不是Person的实例,而是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 serailization不是性能瓶颈(描述您的代码并找出在序列化时花费了多少CPU时间,如果您没有'已经这样做了)然后我会说只使用JavaScriptSerializer。

注入已知类型

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

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

2)将DataContractResolver的子类(以及找到您感兴趣的类型的方法)传递给DataContractSerializer constructor

您可以创建各种类型的“已知类型注册表”,以跟踪可添加到字典中的类型,如果您控制需要注入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. 在构造序列化程序时从寄存器中获取已知类型的数组:

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

    更多动态/更好的选项是可能的,但它们也更难实现,如果您想要阅读有关动态已知类型解析的更多信息,请查看Juval Lowy关于主题here的MSDN文章。 此外,Carlos Figueira的this blog post还详细介绍了更多先进技术,例如动态生成类型,非常值得您阅读主题时阅读!

答案 1 :(得分:1)

免责声明我建议您不要在WCF端点上公开object;虽然这似乎是“灵活的”,但这并不是一个好主意,因为您没有指定您的服务将提供哪种信息。

现在回答

如果您的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;
}

免责声明2 这是达到目的的一种手段(因为您提出了问题) - 对于生产质量的应用程序,我会有一个定义良好的界面,因此很清楚什么是请求并发送回来电线。