使用 WCF 将类序列化为 xsd.exe 生成的 JSON

Posted

技术标签:

【中文标题】使用 WCF 将类序列化为 xsd.exe 生成的 JSON【英文标题】:Serializing a class to JSON generated by xsd.exe using WCF 【发布时间】:2016-05-29 04:51:19 【问题描述】:

我想用 WCF 编写一个 RESTful Web 服务,它能够以 JSON 和 XML 回复。我有一个 XML 模式,我使用xsd.exe 从中生成了我的类。只要我请求 XML,一切都可以正常工作,但如果我想要 JSON 作为响应,它就会失败。

System.ServiceModel.Dispatcher.MultiplexingDispatchMessageFormatter 抛出 System.Collections.Generic.KeyNotFoundException。问题是,到目前为止,我发现xsd.exe 不会生成DataContractDataMember 属性。有没有什么解决方案我不需要使用SvcUtil.exe,因为因此我需要更改我的架构..

那是它失败的代码,JsonDispatchMessageFormatter 的类型是MultiplexingDispatchMessageFormatter。 (反正这是默认类型)

var headers = requestProperty.Headers[HttpRequestHeader.Accept] ?? requestProperty.Headers[HttpRequestHeader.ContentType];
if (headers != null && headers.Contains("application/json"))

    return this.JsonDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);

生成的代码:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="...")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="...", IsNullable=false)]
public partial class Incident         
    private long incidentIdField;

    /// <remarks/>
    public long IncidentId 
        get 
            return this.incidentIdField;
        
        set 
            this.incidentIdField = value;
        
    

【问题讨论】:

在重新调整之前尝试添加WebOperationContext.Current.OutgoingResponse.ContentType = "application/json"; "application/json" 添加到传出响应中允许我现在序列化为json,但我还有一个问题。 XML 和 JSON 都序列化支持字段而不是属性。我可以为我的服务合同使用属性XmlSerializerFormat 来避免XML 的这种情况,但是我再次不能使用JSON,因为它不喜欢这个属性。它建议我使用DataContractFormatAttribute,这将再次导致序列化支持字段.. 【参考方案1】:

您在 JSON 中看到私有字段而不是公共属性的原因是 xsd.exe 已将您的类标记为 [SerializableAttribute]。当此属性应用于一个类型时,它表示该类型的实例可以通过序列化所有私有和公共字段来序列化——而不是属性。

在幕后,WCF 使用DataContractJsonSerializer 与 JSON 进行序列化。当此序列化程序为没有data contract attributes 的类型生成默认的隐式数据协定时,它会注意到[Serializable] 属性并尊重它,生成一个对公共和私有字段进行序列化和反序列化的协定。这就是你所看到的。

有点奇怪的是xsd.exe 不需要将[Serializable] 添加到其生成的类中。 XmlSerializer 完全忽略此属性,因为它只能序列化公共成员。 (它也完全忽略了数据契约属性。同样,数据契约序列化器都忽略了XmlSerializer control attributes。)

不幸的是,我没有看到任何xsd command line switches 来禁用此属性。因此,您将进行某种手动修复:

从生成的类中删除 [System.SerializableAttribute()]。除非您在某处使用BinaryFormatter,否则这应该是无害的;你可能不是。

[DataContract][DataMember] 属性添加到生成的类中。 (请记住,XmlSerializer忽略这些,因此您预先存在的 XML 架构将保持不变。)

您还可以考虑使用SvcUtil.exe 生成符合合同的类,然后使用automapper 将旧类映射到新类。

或者您可以考虑切换到不同的 JSON 序列化程序,例如 json.net,您可以在其中 control whether to ignore or respect [Serializable]。 How to set Json.Net as the default serializer for WCF REST service 和 C# WCF REST - How do you use JSON.Net serializer instead of the default DataContractSerializer? 的问题应该可以帮助您入门。

顺便说一句,如果您想精确控制 XML 和 JSON 格式,WCF rest 可能不是最适合您的技术。 ASP.NET Web API 允许使用json.net 更精确地控制序列化格式;请参阅 JSON and XML Serialization in ASP.NET Web API 以及 Setting IgnoreSerializableAttribute Globally in Json.net 和 .NET WebAPI Serialization k_BackingField Nastiness。

【讨论】:

以上是关于使用 WCF 将类序列化为 xsd.exe 生成的 JSON的主要内容,如果未能解决你的问题,请参考以下文章

在 WCF 服务中使用我们自己的 xsd 文件

从使用 XSD.exe 生成的 XML 中反序列化类

WinRT 与 xsd.exe 生成的序列化/无权访问 System.Xml.XmlNode

C# XML 反序列化为一张表中的 DataSet

使用 xsd.exe 或 svcutil.exe 对 UWP 和 Xamarin 进行 C# XML 序列化

如何使用 Windows Phone 8 SDK 将类序列化为 XML?