Reputation: 445
I want to write a RESTful Webservice with WCF which is able to reply in JSON and XML. I have a XML schema from which I generated my classes by using xsd.exe
. Everthing works fine as long as I request XML, but it fails if I want JSON as response.
The System.ServiceModel.Dispatcher.MultiplexingDispatchMessageFormatter
throws a System.Collections.Generic.KeyNotFoundException
. The problem is, what I found out so far, that xsd.exe
does not generate DataContract
and DataMember
Attributes. Is there any solution to this where I do not need to use the SvcUtil.exe
, because therefore I would need to change my schema..
That is the code where it fails, JsonDispatchMessageFormatter
is of the type MultiplexingDispatchMessageFormatter
. (Which is the default type anyway)
var headers = requestProperty.Headers[HttpRequestHeader.Accept] ?? requestProperty.Headers[HttpRequestHeader.ContentType];
if (headers != null && headers.Contains("application/json"))
{
return this.JsonDispatchMessageFormatter.SerializeReply(messageVersion, parameters, result);
}
Generated code:
[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;
}
}
}
Upvotes: 4
Views: 1669
Reputation: 116544
The reason you are seeing private fields rather than public properties in your JSON is that xsd.exe
has marked your classes with [SerializableAttribute]
. When this attribute is applied to a type, it signifies that instances of the type can be serialized by serializing all private and public fields -- not the properties.
Under the covers, WCF uses DataContractJsonSerializer
to serialize to and from JSON. When this serializer goes to generate a default, implicit data contract for a type without data contract attributes, it notices the [Serializable]
attribute and respects it, generating a contract to serialize and deserialize public and private fields. This is what you are seeing.
What is a bit odd is that xsd.exe
doesn't need to add [Serializable]
to its generated classes. XmlSerializer
completely ignores this attribute since it can only serialize public members. (It also completely ignores data contract attributes. Similarly the data contract serializers all ignore XmlSerializer
control attributes.)
Unfortunately, I don't see any xsd
command line switches to disable this attribute. Thus you're going to do some sort of manual fix:
Remove [System.SerializableAttribute()]
from the generated classes. This should be harmless unless you're using BinaryFormatter
somewhere; you probably are not.
Add [DataContract]
and [DataMember]
attributes to the generated classes. (Remember, these are ignored by XmlSerializer
so you're pre-existing XML schema will be unchanged.)
You might also consider generating contract-appropriate classes with SvcUtil.exe
then using automapper to map your old classes to your new classes.
Or you might consider switching to a different JSON serializer, such as json.net, where you can control whether to ignore or respect [Serializable]
. The questions How to set Json.Net as the default serializer for WCF REST service and C# WCF REST - How do you use JSON.Net serializer instead of the default DataContractSerializer? should get you started.
As an aside, WCF rest may not be the best technology for you if you want precise control over your XML and JSON formatting. ASP.NET Web API allows for much more precise control of serialization formats by using json.net; see JSON and XML Serialization in ASP.NET Web API along with Setting IgnoreSerializableAttribute Globally in Json.net and .NET WebAPI Serialization k_BackingField Nastiness.
Upvotes: 1