【问题标题】:Can I serialize Dictionary<string, object> using DataContract serializer?我可以使用 DataContract 序列化程序序列化 Dictionary<string, object> 吗?
【发布时间】:2012-04-26 11:06:23
【问题描述】:

我计划构建一个 WCFservice,它返回序列化为 JSON 的通用字典对象。不幸的是,序列化失败了,因为对象可能总是不同的。 KnownTypes 无能为力,因为属性类型是 Dictionary,我不能说 KnownType,因为类可能总是不同的。

如果可以序列化“未知类型”有什么想法吗?

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

匿名课程怎么样?

【问题讨论】:

  • 我很高兴人们已经喜欢这样了。请不要费心回答 WCF 是如何不是为此而制作的等等。如果您能告诉我们是否有已知的方式,或者您是否去过那里并出于正当理由放弃了,我将不胜感激(什么原因?)。

标签: json wcf serialization datacontractserializer


【解决方案1】:

注意:如果您只是想了解原始问题中提到的已知类型问题的解决方案,我在回答开始时已经详细介绍了 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&lt;string, object&gt; 的实例并在序列化它之前添加一个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&lt;string, object&gt;,而是一个简单的属性包。

如果您想取回 Person 实例,您可以(尽管您很可能永远不会想要!)使用 ConvertToType 辅助方法转换该 Dictionary&lt;string, object&gt;

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 的所有类型,您可以尝试最简单的事情:

  1. 使用静态方法创建一个KnownTypeRegister 类以将类型添加到已知类型列表中:

    公共静态类 KnownTypeRegister
    {
        私有静态只读 ConcurrentBag _knownTypes = new ConcurrentBag();
        公共静态无效添加(类型类型)
        {
            _knownTypes.Add(type);
        }
        公共静态 IEnumerable Get()
        {
            返回 _knownTypes.ToArray();
        }
    }
  2. 添加一个静态构造函数,用寄存器注册类型:

    [数据合约]
    公共类人
    {
        静态人()
        {
            KnownTypeRegister.Add(typeof(Person));
        }
        [数据成员]
        公共字符串名称 { 获取;放; }
        [数据成员]
        公共 int 年龄 { 得到;放; }
    }
  3. 在构造序列化器时从寄存器中获取已知类型的数组:

var serializer = new DataContractSerializer(typeof(Dictionary&lt;string, object&gt;), KnownTypeRegister.Get());

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

【讨论】:

  • 首先感谢您的详细回复。看起来确实值得研究一下。我还在您的博客上了解了 ServiceStack,构建 REST JSON 服务听起来肯定比其他任何事情都简单得多。
【解决方案2】:

免责声明我不建议您在 WCF 端点上公开object;尽管这看起来很“灵活”,但这不是一个好主意,因为您尚未指定您的服务将提供哪种信息。

现在来回答一下

如果您的 WCF 调用正被 ajax 调用占用,并且正如您所说的 Javascript client just doesn't care.,那么为什么不让您的 WCF 调用简单地返回一个字符串呢?然后你的 WCF 调用的内部可以使用 JavaScriptSerializer 序列化 Dictionary&lt;string, object&gt;

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 这是达到目的的一种手段(因为您提出了这个问题) - 对于生产质量应用程序,我将有一个定义明确的界面,因此很清楚所请求的内容并发送回电线。

【讨论】:

  • 是的,返回一个字符串是有效的点。但是,必须在每种方法中手动进行序列化是不可接受的。我实际上正在为此考虑某种包装器。我只是觉得使用 DataContract 序列化会更快,因为它是框架的一部分。是吗?
  • 但是,Javascript 代码必须准确地知道从原型到生产的整个过程中从请求返回的内容,而我看不出 .NET 中的严格输入有多大帮助。可以吗?
  • tishma,我没有任何关于DataContract 是否会更快的指标。但是,如果使用第三方 JSON 序列化程序(例如 Json.NET)进行序列化并返回一个字符串可能比让 WCF 序列化更快,我不会感到惊讶。不,严格类型对 javascript 方面没有帮助,但是通过严格类型,您可以查看您的“公共 API”并准确查看您公开的内容,而不是公开返回谁知道什么的 Dictionary&lt;string, object&gt;。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-12
  • 1970-01-01
  • 2013-01-16
  • 1970-01-01
  • 2012-07-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多