如何使用DataContractJsonSerializer序列化类类型而不是名称空间到Json字符串

本文关键字:空间 字符串 Json DataContractJsonSerializer 何使用 序列化 类型 | 更新日期: 2023-09-27 17:49:49

我试图在WCF服务中使用DataContractJsonSerializer将类层次结构序列化为Json字符串。序列化派生类的默认行为是向对象添加以下键值对:

"__type":"ClassName:#Namespace"

我的问题是名称空间很长,它们使Json字符串膨胀。我想以某种方式干预序列化并输出以下内容:

"__type":"ClassName"

和反序列化再次介入,指向正确的名称空间(我在运行时知道)。

有办法做这样的事情吗?

如何使用DataContractJsonSerializer序列化类类型而不是名称空间到Json字符串

本页描述了发出__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"。

的(private) WriteServerTypeAttribute方法将__type发送到JSON流中(内部)System.Runtime.Serialization.Json.XmlJsonWriter类。据我所知,没有公开的、有文档的、受支持的方法来修改它。

为了避免这种情况,您可能需要从WCF方法返回一个字符串,自己执行序列化,并对发出的JSON进行后处理。


如果你不介意__type的东西,但只是想从值中删除合格的命名空间,那么把你的类型放在全局命名空间中。换句话说,将它们放在代码中的任何namespace声明之外。

示例:当数据类型驻留在名称空间中时,当我使用派生类型时,序列化的JSON看起来像这样:
{
  "__type":"Subscriber:#My.Custom.Namespace",
  "Index":604455,
  "Name":"Fleming",
  "Id":580540
}

当数据类型位于全局命名空间中时,它看起来像这样:

{
  "__type":"Subscriber:#",
  "Index":708759,
  "Name":"Fleming",
  "Id":675323
}

将名称空间参数添加到数据契约中可以达到目的。[DataContract(Namespace = "")]

Cheeso的回答非常好。我确实发现了一个改进来清理__type字段:

与其将子类从命名空间中移除,不如添加如下属性:

[DataMember(Name = "__type")]
public string SubclassType
{
    get
    {
        return "Subscriber";
    }
    set { }
}

你仍然会被"__type"这个丑陋的名字所困扰,但我发现,因为我返回的是一列子类型,所以我无论如何都想指定类型名称。您甚至可以返回值"以进一步减小响应大小。您也可以直接将属性声明为:

public string __type

,但我发现,强调黑客,所以我坚持使用适当的属性名称,然后重命名它。

-Joey

注意:我在下面输入了这个答案,后来意识到DataContractResolver目前不支持DataContractJsonSerializer。不过,它可能很快就会出现在框架的下一个版本中。如果你要查看的不仅仅是JSON,这也很有用。

* *

您可以使用DataContractResolver来完成此操作,它允许您以自定义方式将类型映射到xsi:type (__type)信息,反之亦然。

要做到这一点,请查看这篇关于DataContractResolver的博客文章,加上这个概念主题,再加上这个示例

@Cheeso writing:

为了避免这种情况,您可能需要从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/en-us/library/bb412179%28v=vs.110%29.aspx中的示例)

注意SomeClass上的[DataContract]没有包含我在其他地方看到的建议的(name="")语法。这样做只会从__type中删除命名空间,代价是需要修饰ALL您的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)我的对象总是至少有一个字段,(b)我发现__type字段总是在序列化对象的输出中第一个。

像往常一样,你可能需要调整一些东西来适应你的情况,但我发现它对我很有效。

前段时间我决定了这个问题。我使用DataContractJsonSerializer如果你的序列化方法有基类参数,但你给它子类作为参数,你将在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 '",'"'",'"Vv '",'"年龄'":10}"

在第二个

:

"{'"'":'"Vv '",'"年龄'":10}"