我打算构建一个WCF服务,返回序列化为JSON的通用字典对象。不幸的是,序列化失败,因为对象可能总是不同。 KnownTypes无法帮助,因为属性类型是Dictionary,我不能说KnownType,因为类可能总是不同。
是否有可能序列化“未知类型”?
我不介意为每个类指定DataContract / DataMember,但(至少对于原型版本)我不希望每个响应都有强类型。 Javascript客户端根本不关心。
匿名课怎么样?
答案 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的所有类型,则可以尝试最简单的事情:
使用静态方法创建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关于主题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 这是达到目的的一种手段(因为您提出了问题) - 对于生产质量的应用程序,我会有一个定义良好的界面,因此很清楚什么是请求并发送回来电线。