Lydon
Lydon

Reputation: 2975

WCF endpoint not serializing XML correctly

I have a WCF endpoint at http://localhost:8090/api/Test. The implementation looks a little like this:

[OperationContract]
[WebInvoke(
    Method = "POST",
    BodyStyle = WebMessageBodyStyle.Bare,
    UriTemplate = "Test")]
void TestEndpoint(Test test);

I have some data objects declared elsewhere

[DataContract]
public class TestBase
{
    [DataMember(Name = "BaseValue")]
    public string BaseValue { get; set; }
}
[DataContract(Namespace = "")]
public class Test : TestBase
{
    [DataMember(Name = "TestValue")]
    public string TestValue { get; set; }
}

The issue that I'm having is when I call the endpoint and pass the object data via the request body, the data is only serialized correctly when I use JSON, not when I use XML.

The following will work fantastic. Test.TestValue == "TestValue" and Test.BaseValue == "BaseValue".

POST http://localhost:8090/api/Test
Content-Type: text/json

{ "TestValue":"Test", "BaseValue": "BaseValue" }

When I do the following Test.TestValue == "TestValue" and Test.BaseValue == null :(

POST http://localhost:8090/api/Test 
Content-Type: text/xml

<Test>  <TestValue>Test</TestValue>  <BaseValue>Base</BaseValue></Test>

Any ideas about what I'm doing wrong here?

Upvotes: 1

Views: 51

Answers (1)

dbc
dbc

Reputation: 116516

First, you need to put the base class in the same namespace as the derived class:

[DataContract(Namespace = "")]
public class TestBase
{
    [DataMember(Name = "BaseValue")]
    public string BaseValue { get; set; }
}

If you don't, BaseValue will actually be in a different namespace, the default namespace chosen by the data contract serializer:

<Test xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <BaseValue xmlns="http://schemas.datacontract.org/2004/07/ClrNamespace">foo</BaseValue>
    <TestValue>bar</TestValue>
</Test>

(JSON doesn't have object namespaces, so this isn't a problem there.)

Next, BaseValue must come before TestValue in the XML, because the data contract serializer is order sensitive. See Data Member Order:

The basic rules for data ordering include:

  • If a data contract type is a part of an inheritance hierarchy, data members of its base types are always first in the order.

  • Next in order are the current type’s data members that do not have the Order property of the DataMemberAttribute attribute set, in alphabetical order.

  • Next are any data members that have the Order property of the DataMemberAttribute attribute set. These are ordered by the value of the Order property first and then alphabetically if there is more than one member of a certain Order value. Order values may be skipped.

Alphabetical order is established by calling the CompareOrdinal method.

(JSON object properties, in contrast, are defined to be unordered, so you won't encounter this complication with DataContractJsonSerializer.)

Thus, having added the namespace declaration on the base class, the following XML can be deserialized:

<Test>  <BaseValue>Base</BaseValue> <TestValue>Test</TestValue>  </Test>

Upvotes: 1

Related Questions