Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does DataContractSerializer work only with http://schemas.datacontract.org/2004/07/

I had a problem that my object wasn't being deserialized from the Post request's body, and I followed this answer which states because DataContractSerializer is used by default in WebApi, you need to define your xmlns this way

<TestModel 
xmlns="http://schemas.datacontract.org/2004/07/YourMvcApp.YourNameSpace">

and indeed it works, but if I change even the year or month, like http://schemas.datacontract.org/2005/07/..., it stops working and my object becomes null again.

Why is that, is http://schemas.datacontract.org/2004/07/ hardcoded somehow? Why this URL, exactly?

like image 694
sashoalm Avatar asked Feb 04 '23 19:02

sashoalm


1 Answers

Every data contract object is identified by a fully-qualified data contract name. As explained in Data Contract Names:

Basic rules regarding naming data contracts include:

  • A fully-qualified data contract name consists of a namespace and a name.
  • Data members have only names, but no namespaces.
  • When processing data contracts, the WCF infrastructure is case-sensitive to both the namespaces and the names of data contracts and data members.

A data contract namespace takes the form of a Uniform Resource Identifier (URI). The URI can be either absolute or relative. By default, data contracts for a particular type are assigned a namespace that comes from the common language runtime (CLR) namespace of that type.

By default, any given CLR namespace (in the format Clr.Namespace) is mapped to the namespace "http://schemas.datacontract.org/2004/07/Clr.Namespace". To override this default, apply the ContractNamespaceAttribute attribute to the entire module or assembly. Alternatively, to control the data contract namespace for each type, set the Namespace property of the DataContractAttribute.

And in Data Contract Equivalence:

For data contracts to be equivalent, they must have the same namespace and name. Additionally, each data member on one side must have an equivalent data member on the other side.

Thus, to successfully send a data contract object over the wire, the fully-qualified data contract names must match on both ends. As mentioned above, the default data contract namespace is http://schemas.datacontract.org/2004/07/Clr.Namespace, but you probably will want to change this to reflect your organization somehow, for instance:

[DataContract(Namespace = "http://schemas.MyOrganization.com/v1")]
public class TestModel 
{
    [DataMember]
    public string Value { get; set; }
}

Or you can set it for your entire assembly and .Net namespace:

[assembly: ContractNamespace("http://schemas.MyOrganization.com/v1", ClrNamespace = "YourMvcApp.YourNameSpace")]

Data contract serialization can be used for both JSON and XML, so how does how does DataContractSerializer map the data contract name from and to XML? It does so using the XML element local name and namespace URI:

<TestModel xmlns="http://schemas.datacontract.org/2004/07/YourMvcApp.YourNameSpace">

The xmlns="http://schemas.datacontract.org/2004/07/YourMvcApp.YourNameSpace" attribute is the default XML namespace declaration of the element with local name TestModel. The local name and namespace together comprise the expanded name of the element. XML element names are considered equal if the namespace URI and local name match, and so Microsoft chose to correspond the data contract name to the XML element local name, and the data contract namespace to the XML namespace URI, which is why changing the even the year or month in the URI causes deserialization to fail.

Thus, as you can see, the choice of namespace needs to be settled before rolling out a data contract Web API or WCF service since changing the namespace requires updating the namespaces on the client side. (Of course, for WCF, clients will typically auto-generate a client from schema metadata as explained, for instance, here or here.)

like image 68
dbc Avatar answered Apr 27 '23 01:04

dbc