问题描述
|
我正在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由(私有)WriteServerTypeAttribute方法发送到JSON流中。
(内部)System.Runtime.Serialization.Json.XmlJsonWriter类。据我所知,没有公开的,有文档记录的,受支持的方法可以对此进行修改。
为避免这种情况,您可能需要从WCF方法返回一个字符串,自己执行序列化,然后对发出的JSON进行后处理。
如果您不介意__type事情,而只想从值中删除合格的名称空间,则将您的类型放入全局名称空间。换句话说,将它们放在代码中的任何“ 6”声明之外。
示例:当数据类型驻留在名称空间中时,并且当我使用派生类型时,序列化的JSON如下所示:
{
\"__type\":\"Subscriber:#My.Custom.Namespace\",\"Index\":604455,\"Name\":\"Fleming\",\"Id\":580540
}
当数据类型位于全局名称空间中时,它看起来像这样:
{
\"__type\":\"Subscriber:#\",\"Index\":708759,\"Id\":675323
}
,将命名空间参数添加到数据协定即可解决问题。
[DataContract(Namespace = \"\")]
,Cheeso的回答非常好。我确实发现了一种清理__type字段的改进方法:
除了可以从其名称空间中删除子类之外,还可以添加如下属性:
[DataMember(Name = \"__type\")]
public string SubclassType
{
get
{
return \"Subscriber\";
}
set { }
}
您仍然对丑陋的名称“ __type \”感到困惑,但是我发现由于我要返回子类型列表,所以无论如何我都想指定类型名称。您甚至可以返回值\“ \”以进一步减小响应大小。您也可以将属性声明为:
public string __type
但是我发现这很容易引起黑客的注意,所以我坚持使用适当的属性名称,然后将其重命名。
-乔伊
,注意:我在下面输入了此答案,后来意识到DataContractJsonSerializer当前不支持DataContractResolver。但是,它可能很快就会发布在该框架的下一个版本中。如果您不仅在查看JSON,这也很有用。
**
您可以使用DataContractResolver做到这一点,它可以将类型映射到xsi:type(__type)信息,反之亦然。
为此,请查看有关DataContractResolver的博客文章,此概念性主题以及此示例。
,@Cheeso写道:
为避免这种情况,您可能需要从WCF返回一个字符串
方法,自己执行序列化,然后对
发出JSON。
这是我实现该后处理的方式。我以为我会在这里发布它,它可能会对其他人有所帮助。
首先,介绍一下如何生成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/zh-cn/library/bb412179%28v=vs.110%29.aspx的示例)
请注意,“ 14”上的“ 13”不包含我在其他地方看到的“ 15”语法。这样只会从__type中删除名称空间,但需要装饰所有的DataContract属性,这会使您的代码混乱。相反,我的后处理器在__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 class参数,但您将其subClass作为参数,则json中将具有__type。
更多细节:
[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<T>(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>(T type)
{
Console.WriteLine(ToJson(type));
}
在第一次测试中,
\“ {\\\” __ type \\\“:\\\” B:#ConsoleApplication1 \\\“,\\\” S \\\“:\\\” Vv \\\“,\\\”年龄\\\“:10} \”
第二:
\“ {\\\” S \\\“:\\\” Vv \\\“,\\\”年龄\\\“:10} \”