简体   繁体   English

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

[英]How to serialize class type but not the namespace to a Json string using DataContractJsonSerializer

I'm trying to serialize a class hierarchy to a Json string using DataContractJsonSerializer , in a WCF service. 我正在尝试在WCF服务中使用DataContractJsonSerializer将类层次结构序列化为Json字符串。 the default behaviour for serializing a derived class is to add the following key value pair to the object: 序列化派生类的默认行为是将以下键值对添加到对象:

"__type":"ClassName:#Namespace"

My problem is that namespaces are long and they bloat the Json string. 我的问题是命名空间很长并且它们使Json字符串膨胀。 I would like to somehow intervene with the serialization and output this instead: 我想以某种方式干预序列化并输出它:

"__type":"ClassName"

and on deserialization intervene again to point to the correct namespace (which i know in runtime). 并且在反序列化时再次介入指向正确的命名空间(我在运行时知道)。

Is there any way to do such a thing? 有没有办法做这样的事情?

This page describes the circumstances under which the __type property is emitted. 此页面描述了发出__type属性的环境。 In short, in WCF, if you use a derived type, and a KnownTypeAttribute, then you're going to get a __type property. 简而言之,在WCF中,如果使用派生类型和KnownTypeAttribute,那么您将获得__type属性。

Example: 例:

Assume 假设

[DataContract]
[KnownType(typeof(Subscriber))]
public class Person { ... }

[DataContract]
public class Subscriber : Person { ... } 

This code generates a __type property: 此代码生成__type属性:

    var o = new Subscriber("Fleming");
    var serializer = new DataContractJsonSerializer(typeof(Person));
    serializer.WriteObject(Console.OpenStandardOutput(), o);

But this code does not: 但是这段代码没有:

    var o = new Subscriber("Fleming");
    var serializer = new DataContractJsonSerializer(typeof(Subscriber));
    serializer.WriteObject(Console.OpenStandardOutput(), o);

Notice that the second snip uses a DCJS with the same type as the object being serialized. 请注意,第二个剪辑使用的DCJS与要序列化的对象的类型相同。

To avoid the __type, don't use derived types, or to be precise, use a serializer typed to the type you are actually serializing. 要避免__type,请不要使用派生类型,或者确切地说,使用类型化的序列化程序来实际序列化。 If the serialization is being performed implicitly by a WCF method, then the method must be typed appropriately. 如果通过WCF方法隐式执行序列化,则必须适当地键入该方法。 In my example, it means you must use a return type of "Subscriber", and not the parent type, "Person". 在我的示例中,这意味着您必须使用返回类型“订阅者”,而不是父类型“人”。

The __type is emitted into the JSON stream by the (private) WriteServerTypeAttribute method on the (internal) System.Runtime.Serialization.Json.XmlJsonWriter class. __type由(内部)System.Runtime.Serialization.Json.XmlJsonWriter类上的(私有)WriteServerTypeAttribute方法发送到JSON流中。 There is no public, documented, supported way to modify that, as far as I can tell. 据我所知,没有公开的,有文档记录的,支持的方式来修改它。

To avoid this, you'd maybe need to return a string from the WCF method, perform the serialization yourself, and post-process the emitted JSON. 为避免这种情况,您可能需要从WCF方法返回一个字符串,自己执行序列化,并对发出的JSON进行后处理。


If you don't mind the __type thing, but just want to remove the qualifying namespace from the value, then put your types in the global namespace. 如果您不介意__type事物,但只想从值中删除限定命名空间,那么将您的类型放在全局命名空间中。 In other words, put them outside of any namespace declaration in code. 换句话说,将它们放在代码中的任何namespace声明之外。

Example: When the data types reside in a namespace, and when I used a derived type, the serialized JSON looks like this: 示例:当数据类型驻留在命名空间中时,当我使用派生类型时,序列化的JSON如下所示:

{
  "__type":"Subscriber:#My.Custom.Namespace",
  "Index":604455,
  "Name":"Fleming",
  "Id":580540
}

When the data types reside in the global namespace, it looks like this: 当数据类型驻留在全局命名空间中时,它看起来像这样:

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

Adding the namespace parameter to the data contract does the trick. 将namespace参数添加到数据协定就可以了。 [DataContract(Namespace = "")]

Cheeso's answer was excellent. Cheeso的答案很棒。 I did discover a refinement to cleaning up the __type field though: 我确实发现了清理__type字段的一个改进:

Rather than removing your subclass from its namespace you can add a property like the following: 您可以添加如下属性,而不是从其命名空间中删除子类:

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

You still get stuck with the ugly name "__type" but I found that because I was returning a list of subtypes I wanted to specify the type name anyway. 你仍然坚持使用丑陋的名字“__type”,但我发现因为我正在返回一个子类型列表,所以我想指定类型名称。 You could even return a value of "" to further reduce response size. 您甚至可以返回值“”以进一步减小响应大小。 You could also just declare the property as: 您也可以将该属性声明为:

public string __type

but I found that to accentuate the hack so I stuck with an appropriate property name and then renamed it. 但我发现要强调黑客攻击,所以我坚持使用适当的属性名称,然后将其重命名。

-Joey -Joey

@Cheeso wrote: @Cheeso写道:

To avoid this, you'd maybe need to return a string from the WCF method, perform the serialization yourself, and post-process the emitted JSON. 为避免这种情况,您可能需要从WCF方法返回一个字符串,自己执行序列化,并对发出的JSON进行后处理。

Here's how I implemented that post-processing. 这是我实现后处理的方式。 I thought I'd post it here JIC it might help someone else. 我以为我会在JIC发布它可能会帮助其他人。

First some boilerplate to show how I generate my JSON string: 首先是一些样板来展示我如何生成我的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();

(Serialization is based on examples from https://msdn.microsoft.com/en-us/library/bb412179%28v=vs.110%29.aspx ) (序列化基于https://msdn.microsoft.com/en-us/library/bb412179%28v=vs.110%29.aspx中的示例)

Note that the [DataContract] on SomeClass does not include the (name="") syntax that I've seen suggested elsewhere. 请注意, SomeClass上的[DataContract] 包括我在别处看到的(name="")语法。 That only removes the namespace from the __type at the cost of needing to adorn ALL your DataContract attrs, which clutters your code. 这只会从__type中删除命名空间,代价是需要装饰所有的DataContract attrs,这会使代码变得混乱。 Instead, my post-processor handles the assembly name in the __type field. 相反,我的后处理器在__type字段中处理程序集名称。

And now the string json contains the JSON text, but unfortunately includes all that "__type" junk that you don't want but can't suppress. 现在字符串json包含JSON文本,但遗憾的是包含了你不想要但却无法抑制的所有“__type”垃圾。

So here's my post-processing code that removes it: 所以这是我删除它的后处理代码:

// 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, "");
    }
}

This makes a few assumptions, most notably that the __type field ends with a comma, which assumes that another field follows it, though (a) my objects always have at least 1 field and (b) I've found that the __type field is always 1st in the serialized object's output. 这做了一些假设,最值得注意的是__type字段以逗号结尾,假设另一个字段跟随它,但是(a)我的对象总是至少有1个字段,(b)我发现__type字段是始终在序列化对象的输出中的第一个。

As always, you may have to adjust something to your situation, but I find it works well for mine. 和往常一样,你可能需要根据自己的情况调整一些东西,但我发现它适用于我的情况。

Some times ago i decided this problem. 不久前我决定了这个问题。 I use DataContractJsonSerializer You will have __type in json, if your method for serialization have Base class parameter, but you give it subClass as parameter. 我使用DataContractJsonSerializer如果你的序列化方法有Base类参数,你将在json中有__type,但你给它subClass作为参数。 More details: 更多细节:

[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());
    }
}

You have two methods for test: 您有两种测试方法:

public static void ReadTypeDerived(A type)
{
    Console.WriteLine(ToJson(type));
}

and

public static void ReadType<T>(T type)
{
    Console.WriteLine(ToJson(type));
}

In first test you wiil have 在第一次测试中你会有

"{\\"__type\\":\\"B:#ConsoleApplication1\\",\\"S\\":\\"Vv\\",\\"Age\\":10}" “{\\” __类型\\ “:\\” B:#ConsoleApplication1 \\ “\\ ”S \\“:\\ ”VV \\“,\\ ”年龄\\“:10}”

In second: 第二个:

"{\\"S\\":\\"Vv\\",\\"Age\\":10}" “{\\” S \\ “:\\” VV \\ “\\ ”年龄\\“:10}”

Note: I typed up this answer below and later realized that DataContractResolver is currently not supported with DataContractJsonSerializer. 注意:我在下面输入了这个答案后来意识到DataContractJsonSerializer当前不支持DataContractResolver。 It may soon be with the next release of the framework, however. 然而,它可能很快就会出现在框架的下一个版本中。 This is also useful if you are looking at more than just JSON. 如果您不仅仅关注JSON,这也很有用。

** **

You can do this with a DataContractResolver, which lets you map types to xsi:type (__type) information and vice-versa in a custom manner. 您可以使用DataContractResolver执行此操作,它允许您以自定义方式将类型映射到xsi:type(__type)信息,反之亦然。

To do this, check out this blog post on DataContractResolver , plus this conceptual topic , plus this sample 要执行此操作,请查看有关DataContractResolver的此博客文章 ,以及此概念主题以及此示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 DataContractJsonSerializer 序列化包含日期和时间属性的 JSON 字符串? - How to serialize JSON string containing date and time property using DataContractJsonSerializer? 无法使用DataContractJsonSerializer将对象序列化为JSON - Unable to Serialize Object to JSON Using DataContractJsonSerializer 使用DataContractJsonSerializer作为JSON数组序列化对象 - Serialize an object using DataContractJsonSerializer as a json array 使用 DataContractJsonSerializer 将字典序列化为 JSON 对象 - Serialize a Dictionary as JSON object using DataContractJsonSerializer 如何使用DataContractJsonSerializer序列化批量数据? - How can serialize bulk data by using DataContractJsonSerializer? 如何使用DataContractJsonSerializer将字符串数组序列化为JSON? - How can I serialise a string array to JSON using DataContractJsonSerializer? 如何使用DataContractJsonSerializer将“12/19/2013 12:00:00 AM”序列化为C#上的DateTime类型 - How to serialize “12/19/2013 12:00:00 AM” to DateTime Type on C# using DataContractJsonSerializer JSON使用.NET DataContractJsonSerializer序列化器与字典进行序列化/反序列化 - JSON serialize/deserialize with Dictionary using .NET DataContractJsonSerializer serializer 使用DataContractJsonSerializer反序列化变量Type JSON数组 - Deserializing variable Type JSON array using DataContractJsonSerializer 如何获取JSON中的__type键以首先使用DataContractJsonSerializer - How to get the __type key in json to be first for DataContractJsonSerializer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM