16

I have a web service that returns the following data:

<?xml version=""1.0"" encoding=""UTF-8""?>
<RESPONSE>
    <KEY>12345</KEY>
    <PROPERTY>
        <PROPERTY_ADDRESS>
            <STREET_NUM>25</STREET_NUM>
            <STREET_ADDRESS>ELM ST</STREET_ADDRESS>
            <STREET_PREFIX/>
            <STREET_NAME>ELM</STREET_NAME>
            <STREET_TYPE>ST</STREET_TYPE>
            <STREET_SUFFIX/>
        </PROPERTY_ADDRESS>
    </PROPERTY>
</RESPONSE>

I have a class structure to match:

[DataContract(Name="RESPONSE", Namespace="")]
public class Response
{
    [DataMember(Name="KEY")]
    public string Key { get; set; }

    [DataMember(Name = "PROPERTY")]
    public Property Property { get; set; }
}

[DataContract(Name="PROPERTY", Namespace="")]
public class Property
{
    [DataMember(Name="PROPERTY_ADDRESS")]
    public PropertyAddress Address { get; set; }
}


[DataContract(Name="PROPERTY_ADDRESS", Namespace="")]
public class PropertyAddress
{
    [DataMember(Name="STREET_NUM")]
    public string StreetNumber { get; set; }

    [DataMember(Name = "STREET_ADDRESS")]
    public string StreetAddress { get; set; }

    [DataMember(Name = "STREET_PREFIX")]
    public string StreetPrefix { get; set; }

    [DataMember(Name = "STREET_NAME")]
    public string StreetName { get; set; }

    [DataMember(Name = "STREET_TYPE")]
    public string StreetType { get; set; }

    [DataMember(Name = "STREET_SUFFIX")]
    public string StreetSuffix { get; set; }
}

My deserialization code looks like this:

[Test]
public void TestMapping()
{
    var serializer = new DataContractSerializer(typeof(Response));

    Response response = null;

    using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(XmlData)))
    {
        response = (Response)serializer.ReadObject(ms);
    }

    //This works
    Assert.AreEqual("12345", response.Key);

    //This works
    Assert.AreEqual("25", response.Property.Address.StreetNumber);

    //This FAILS. StreetAddress is null
    Assert.AreEqual("ELM ST", response.Property.Address.StreetAddress);
}

For the life of me I can't figure out why StreetAddress is failing. It's got to be something simple that I'm missing.

Micah
  • 111,873
  • 86
  • 233
  • 325

2 Answers2

26

DataContractSerializer expects things to be in alphabetical order. You need to add Order to your Data Members for this to work correctly.

[DataContract(Name = "PROPERTY_ADDRESS", Namespace = "")]
public class PropertyAddress
{
    [DataMember(Name = "STREET_NUM", Order=0)]
    public string StreetNumber { get; set; }

    [DataMember(Name = "STREET_ADDRESS", Order=1)]
    public string StreetAddress { get; set; }

    [DataMember(Name = "STREET_PREFIX", Order=2)]
    public string StreetPrefix { get; set; }

    [DataMember(Name = "STREET_NAME", Order=3)]
    public string StreetName { get; set; }

    [DataMember(Name = "STREET_TYPE", Order=4)]
    public string StreetType { get; set; }

    [DataMember(Name = "STREET_SUFFIX",Order=5)]
    public string StreetSuffix { get; set; }
}
Community
  • 1
  • 1
vcsjones
  • 138,677
  • 31
  • 291
  • 286
  • @Micah: Its for performance reason. By expecting the tags in a certain order DataContractSerializer can save time on matching the contract's tags with the message tags. And there has to be some default order if none is specified explicitly. – Vizu Mar 14 '13 at 16:47
  • 22
    I think that any 'performance' gains are more than offset by the sheer amount of wasted developer time in trying to find, debug, and fix this silly requirement. – Carl Onager Jul 01 '13 at 08:29
  • So... how do you disable this behavior? – BrainSlugs83 Sep 27 '13 at 22:30
  • @BrainSlugs83 Not easily. The best thing at this point is to just set the `Order` property on the attribute. You can use `IDispatchMessageInspector.AfterReceiveRequest` to re-format the incoming request, or use a different serialization library. – vcsjones Sep 29 '13 at 13:35
  • 2
    This is really frustrating, I just spent 3 hours figuring out that was the problem. – SecureTeam Software Oct 01 '13 at 17:59
  • 2
    @EranD. I also just spent about 3 hours before I finally came to find this out...FML. And Microsoft wonders why developers become disillusioned with their products. – crush Mar 10 '14 at 18:59
  • 10
    I want to give an upvote for the accuracy and usefulness of your answer, but a downvote for the awful truth it contains. ;) – teedyay Jul 09 '14 at 09:28
  • I spent hours to figure this hour. I was why the hell the compiler is behaving so discriminatory that reads all the other xml elements, except this newly added one. Thanks. [tears of joy on my face right now] – Ben Aug 01 '14 at 14:39
7

You have to augment your data contract with the order of the elements because DataContractSerializer expects the elements to be sorted alphabetically by default. Which is not the case with your XML.

Here's the code:

[DataContract(Name = "PROPERTY_ADDRESS", Namespace = "")]
public class PropertyAddress
{
    [DataMember(Name = "STREET_NUM", Order=1)]
    public string StreetNumber { get; set; }

    [DataMember(Name = "STREET_ADDRESS", Order=2)]
    public string StreetAddress { get; set; }

    [DataMember(Name = "STREET_PREFIX", Order=3)]
    public string StreetPrefix { get; set; }

    [DataMember(Name = "STREET_NAME", Order=4)]
    public string StreetName { get; set; }

    [DataMember(Name = "STREET_TYPE", Order=5)]
    public string StreetType { get; set; }

    [DataMember(Name = "STREET_SUFFIX", Order=6)]
    public string StreetSuffix { get; set; }
}
Vizu
  • 1,871
  • 1
  • 15
  • 21