C# 如何使用DataContractJsonSerializer将类类型而不是命名空间序列化为Json字符串
我试图在WCF服务中使用C# 如何使用DataContractJsonSerializer将类类型而不是命名空间序列化为Json字符串,c#,wcf,json,serialization,datacontractserializer,C#,Wcf,Json,Serialization,Datacontractserializer,我试图在WCF服务中使用DataContractJsonSerializer将类层次结构序列化为Json字符串。 序列化派生类的默认行为是向对象添加以下键值对: “uu类型”:“类名:#名称空间” 我的问题是名称空间很长,并且会使Json字符串膨胀。 我想以某种方式干预序列化并输出以下内容: “\uuu类型”:“ClassName” 在反序列化时,再次介入以指向正确的名称空间(我在运行时知道) 有什么方法可以做到这一点吗?描述了发出_类型属性的情况。简而言之,在WCF中,如果您使用派生类型和Kn
DataContractJsonSerializer
将类层次结构序列化为Json字符串。
序列化派生类的默认行为是向对象添加以下键值对:
“uu类型”:“类名:#名称空间”
我的问题是名称空间很长,并且会使Json字符串膨胀。
我想以某种方式干预序列化并输出以下内容:
“\uuu类型”:“ClassName”
在反序列化时,再次介入以指向正确的名称空间(我在运行时知道)
有什么方法可以做到这一点吗?描述了发出_类型属性的情况。简而言之,在WCF中,如果您使用派生类型和KnownTypeAttribute,那么您将获得一个_类型属性
例如:
假定
[DataContract]
[KnownType(typeof(Subscriber))]
public class Person { ... }
[DataContract]
public class Subscriber : Person { ... }
此代码生成一个_类型属性:
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);
请注意,第二个snip使用的DCJS与被序列化的对象具有相同的类型
要避免使用_类型,请不要使用派生类型,或者准确地说,请使用与实际序列化的类型对应的序列化程序。如果序列化是由WCF方法隐式执行的,则必须适当地键入该方法。在我的示例中,这意味着您必须使用返回类型“Subscriber”,而不是父类型“Person”
_u_类型由上的(私有)WriteServerTypeAttribute方法发送到JSON流中
(内部)System.Runtime.Serialization.Json.XmlJsonWriter类。据我所知,没有公开的、有文档记录的、受支持的方法来修改它
为了避免这种情况,您可能需要从WCF方法返回一个字符串,自己执行序列化,然后对发出的JSON进行后期处理
如果您不介意_类型,但只想从值中删除限定的名称空间,那么将您的类型放入全局名称空间。换句话说,将它们放在代码中任何
命名空间
声明之外
示例:当数据类型驻留在命名空间中,并且当我使用派生类型时,序列化的JSON如下所示:
{
"__type":"Subscriber:#My.Custom.Namespace",
"Index":604455,
"Name":"Fleming",
"Id":580540
}
{
"__type":"Subscriber:#",
"Index":708759,
"Name":"Fleming",
"Id":675323
}
当数据类型驻留在全局名称空间中时,它如下所示:
{
"__type":"Subscriber:#My.Custom.Namespace",
"Index":604455,
"Name":"Fleming",
"Id":580540
}
{
"__type":"Subscriber:#",
"Index":708759,
"Name":"Fleming",
"Id":675323
}
注意:我在下面输入了这个答案,后来意识到DataContractJsonSerializer当前不支持DataContractResolver。不过,该框架的下一个版本可能很快就会发布。如果您关注的不仅仅是JSON,那么这也很有用 ** 您可以使用DataContractResolver实现这一点,它允许您以自定义方式将类型映射到xsi:type(_-type)信息,反之亦然
为此,请查看,plus,plus将名称空间参数添加到数据协定中即可。
[DataContract(Namespace=”“)]
奇索的回答非常好。不过,我确实发现了清理_类型字段的改进:
您可以添加如下属性,而不是从名称空间中删除子类:
[DataMember(Name = "__type")]
public string SubclassType
{
get
{
return "Subscriber";
}
set { }
}
您仍然会被难看的名称“\uuuu type”所困扰,但我发现,因为我返回了一个子类型列表,所以我还是希望指定类型名称。您甚至可以返回值“”以进一步减小响应大小。您也可以将该属性声明为:
public string __type
但我发现,为了强调这一点,我坚持使用一个合适的属性名称,然后将其重命名
-乔伊@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();
(序列化基于中的示例)
请注意,SomeClass
上的[DataContract]
不包含我在别处看到的(name=”“)
语法。这只会从_类型中删除名称空间,代价是需要修饰DataContract属性,这会使代码变得混乱。相反,我的后处理器在_类型字段中处理程序集名称
现在字符串json
包含了json文本,但不幸的是,它包含了所有您不想要但无法抑制的“_类型”垃圾
下面是我删除它的后处理代码:
// 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, "");
}
}
这做了一些假设,最明显的是_类型字段以逗号结尾,它假设后面有另一个字段,尽管(a)我的对象总是至少有一个字段,(b)我发现_类型字段在序列化对象的输出中总是第一个
和往常一样,你可能需要根据自己的情况调整一些东西,但我发现它对我的情况很有效。前几次我决定解决这个问题。 我使用DataContractJsonSerializer 如果序列化方法具有基类参数,但您将其子类作为参数,那么json中将有_类型。 更多详情:
[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());
}
}
及
publicstaticvoidreadtype(T类型)
{
WriteLine(ToJson(type));
}
在第一次测试中你会
{\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
第二:
{\'S\':\'Vv\',\'Age\':10}
这抑制了属性的名称空间部分,但不抑制跟踪类名的冒号。因此,+1可以节省一些成本,但仍然不是一个完整的解决方案。但是,如果问题是该对象中使用了子类型,该怎么办?请注意,如果完整文件中有多个级别的自定义对象,则需要递归调用它。最后,我用一个正则表达式将它剥离了:
“\”\uuu type\“\\s*:\\s*”[^\“]+\“\\s*,?\\s*”
在没有名称空间的情况下发送看起来非常不正常。不过,我要么干脆把它全部删除,要么就不发送