如何使用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由(私有)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} \”