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

时间:2011-04-13 08:30:04

标签: c# wcf json serialization datacontractserializer

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

"__type":"ClassName:#Namespace"

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

"__type":"ClassName"

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

有没有办法做这样的事情?

6 个答案:

答案 0 :(得分:12)

This page描述了发出__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方法隐式执行序列化,则必须适当地键入该方法。在我的示例中,这意味着您必须使用返回类型“订阅者”,而不是父类型“人”。

__type由(私有)WriteServerTypeAttribute方法发送到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
}

答案 1 :(得分:8)

将namespace参数添加到数据协定中就可以了。 [DataContract(Namespace = "")]

答案 2 :(得分:6)

Cheeso的回答很棒。我确实发现了清理__type字段的一个改进:

您可以添加如下所示的属性,而不是从其命名空间中删除子类:

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

你仍然坚持使用丑陋的名字“__type”,但我发现因为我正在返回一个子类型列表,所以我想指定类型名称。您甚至可以返回值“”以进一步减小响应大小。您也可以将该属性声明为:

public string __type

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

-Joey

答案 3 :(得分:0)

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

**

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

为此,请查看this blog post on DataContractResolver,加this conceptual topic,再加this sample

答案 4 :(得分:0)

@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

中的示例

请注意,[DataContract]上的SomeClass 包含我在别处看到的(name="")语法。这只会从__type中删除命名空间,代价是需要装饰你的DataContract attrs ALL ,这会使你的代码变得混乱。相反,我的后处理器在__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字段在序列化对象的输出中始终为1st。

与往常一样,你可能需要根据自己的情况调整一些东西,但我发现它适合我的。

答案 5 :(得分:0)

前几天我决定了这个问题。 我使用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<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));
}

在第一次测试中你将有

  

&#34; {\&#34; __类型\&#34;:\&#34; B:#ConsoleApplication1 \&#34; \&#34; S \&#34;:\&# 34; VV \&#34; \&#34;年龄\&#34; 10}&#34;

第二名:

  

&#34; {\&#34; S \&#34;:\&#34; VV \&#34; \&#34;年龄\&#34; 10}&#34;