如何使用DataContractJsonSerializer将类类型而不是命名空间序列化为Json字符串
我正在尝试在WCF服务中使用DataContractJsonSerializer
将类层次结构序列化为Json字符串。 序列化派生类的默认行为是将以下键值对添加到对象:
"__type":"ClassName:#Namespace"
我的问题是命名空间很长并且它们使Json字符串膨胀。 我想以某种方式干预序列化并输出它:
"__type":"ClassName"
并且在反序列化时再次介入指向正确的命名空间(我在运行时知道)。
有没有办法做这样的事情?
此页面描述了发出__type属性的情况。 简而言之,在WCF中,如果使用派生类型和KnownTypeAttribute,那么您将获得__type属性。
例:
假设
[DataContract] [KnownType(typeof(Subscriber))] public class Person { ... } [DataContract] public class Subscriber : Person { ... }
此代码生成__type属性:
var o = new Subscriber("Fleming"); var serializer = new DataContractJsonSerializer(typeof(Person)); serializer.WriteObject(Console.OpenStandardOutput(), o);
但是这段代码没有:
var o = new Subscriber("Fleming"); var serializer = new DataContractJsonSerializer(typeof(Subscriber)); serializer.WriteObject(Console.OpenStandardOutput(), o);
请注意,第二个剪辑使用的DCJS与要序列化的对象的类型相同。
要避免使用__type,请不要使用派生类型,或者确切地说,使用类型化的序列化程序来实际序列化。 如果通过WCF方法隐式执行序列化,则必须适当地键入该方法。 在我的示例中,这意味着您必须使用返回类型“Subscriber”,而不是父类型“Person”。
__type由(内部)System.Runtime.Serialization.Json.XmlJsonWriter类上的(私有)WriteServerTypeAttribute方法发送到JSON流中。 据我所知,没有公开的,有文档记录的,支持的方式来修改它。
为避免这种情况,您可能需要从WCF方法返回一个字符串,自己执行序列化,并对发出的JSON进行后处理。
如果你不介意__type的东西,但只是想从值中删除合格的命名空间,那么将你的类型放在全局命名空间中。 换句话说,将它们放在代码中的任何namespace
声明之外。
示例:当数据类型驻留在命名空间中,并且当我使用派生类型时,序列化的JSON如下所示:
{ "__type":"Subscriber:#My.Custom.Namespace", "Index":604455, "Name":"Fleming", "Id":580540 }
当数据类型驻留在全局命名空间中时,它看起来像这样:
{ "__type":"Subscriber:#", "Index":708759, "Name":"Fleming", "Id":675323 }
将namespace参数添加到数据协定就可以了。 [DataContract(Namespace = "")]
Cheeso的回答很棒。 我确实发现了清理__type字段的一个改进:
您可以添加如下属性,而不是从其名称空间中删除子类:
[DataMember(Name = "__type")] public string SubclassType { get { return "Subscriber"; } set { } }
你仍然坚持丑陋的名字“__type”,但我发现,因为我返回了一个子类型列表,我想指定类型名称。 您甚至可以返回值“”以进一步减小响应大小。 您也可以将该属性声明为:
public string __type
但我发现要强调黑客攻击,所以我坚持使用适当的属性名称,然后将其重命名。
-Joey
注意:我在下面输入了这个答案后来意识到DataContractJsonSerializer当前不支持DataContractResolver。 然而,它可能很快就会出现在框架的下一个版本中。 如果您不仅仅关注JSON,这也很有用。
**
您可以使用DataContractResolver执行此操作,它允许您以自定义方式将类型映射到xsi:type(__type)信息,反之亦然。
要执行此操作,请查看有关DataContractResolver的此博客文章 ,以及此概念主题以及此示例
@Cheeso写道:
为避免这种情况,您可能需要从WCF方法返回一个字符串,自己执行序列化,并对发出的JSON进行后处理。
这是我实现后处理的方式。 我以为我会在JIC发布它可能会帮助其他人。
首先是一些样板文件,展示我如何生成我的JSON字符串:
// Instantiate & populate the object to be serialized to JSON SomeClass xyz = new SomeClass(); ... populate it ... // Now serialize it DataContractJsonSerializer ser = new DataContractJsonSerializer(xyz.GetType()); // Note xyz.GetType() ... serialize the object to json, many steps omitted here for brevity ... string json = sr.ReadToEnd();
(序列化基于https://msdn.microsoft.com/en-us/library/bb412179%28v=vs.110%29.aspx中的示例)
请注意, SomeClass
上的[DataContract]
不包括我在别处看到的(name="")
语法。 这只会从__type中删除命名空间,代价是需要装饰所有的DataContract attrs,这会使代码变得混乱。 相反,我的后处理器处理__type字段中的程序集名称。
现在字符串json
包含JSON文本,但遗憾的是包含了你不想要但却无法抑制的所有“__type”垃圾。
所以这是我删除它的后处理代码:
// This strips out that unsuppressable __type clutter generated by the KnownType attributes Attribute[] attrs = Attribute.GetCustomAttributes(xyz.GetType()); foreach (Attribute attr in attrs) { if (attr is KnownTypeAttribute) { KnownTypeAttribute a = (KnownTypeAttribute)attr; string find = "\"__type\":\"" + a.Type.ReflectedType.Name + "." + a.Type.Name + ":#" + a.Type.Namespace + "\","; json = json.Replace(find, ""); } }
这做了一些假设,最值得注意的是__type字段以逗号结尾,这假设另一个字段跟随它,但是(a)我的对象总是至少有1个字段,(b)我发现__type字段是始终在序列化对象的输出中为第1位。
和往常一样,你可能需要根据自己的情况调整一些东西,但我发现它适用于我的情况。
不久前我决定了这个问题。 我使用DataContractJsonSerializer如果你的序列化方法有Base类参数,你将在json中有__type,但你给它subClass作为参数。 更多细节:
[DataContract] [KnownType(typeof(B))] public abstract class A { [DataMember] public String S { get; set; } } [DataContract] public class B : A { [DataMember] public Int32 Age { get; set; } } public static String ToJson(this T value) { var serializer = new DataContractJsonSerializer(typeof(T)); using (var stream = new MemoryStream()) { serializer.WriteObject(stream, value); return Encoding.UTF8.GetString(stream.ToArray()); } }
您有两种测试方法:
public static void ReadTypeDerived(A type) { Console.WriteLine(ToJson(type)); }
和
public static void ReadType(T type) { Console.WriteLine(ToJson(type)); }
在第一次测试中你会有
“{\” __类型\ “:\” B:#ConsoleApplication1 \ “\ ”S \“:\ ”VV \“,\ ”年龄\“:10}”
第二个:
“{\” S \ “:\” VV \ “\ ”年龄\“:10}”