2

I have been following some online examples and for some reason I cannot successfully deserialize the following XML document:

<?xml version="1.0" encoding="UTF-8"?>
<result>
    <postcode>BD1 1LT</postcode>
    <geo>
        <lat>53.793063240118215</lat>
        <lng>-1.7540318423563699</lng>
        <easting>416300.0</easting>
        <northing>433000.0</northing>
        <geohash>http://geohash.org/gcwf00dz21g1</geohash>
    </geo>
    <administrative>
        <council>
            <title>Bradford</title>
            <uri>http://statistics.data.gov.uk/id/statistical-geography/E08000032</uri>
            <code>E08000032</code>
        </council>
        <ward>
            <title>City</title>
            <uri>http://statistics.data.gov.uk/id/statistical-geography/E05001347</uri>
            <code>E05001347</code>
        </ward>
        <constituency>
            <title>Bradford West</title>
            <uri>http://statistics.data.gov.uk/id/statistical-geography/E14000589</uri>
            <code>E14000589</code>
        </constituency>
    </administrative>
</result>

I've tried all sorts of combinations:

[DataContract(Name = "result", Namespace = "")]
public class Result
{
    [DataMember(Name = "postcode")]
    public string Postcode { get; set; }
    [DataMember(Name = "geo")]
    public Geo Geo { get; set; } 
}

[DataContract(Name = "geo")]
public class Geo
{
    [DataMember(Name = "lat")]
    public string Lat { get; set; } 
}

But all I can get is the post code and nothing else. Above is what I have working atm.

Below is what I have put together following some examples:

    [Test]
    public void TestRestCall()
    {
        var url = "http://uk-postcodes.com/postcode/";
        var urlParameters = "bd11lt.xml";
        var client = new HttpClient();
        client.BaseAddress = new Uri(url);

        // Add an Accept header for JSON format.
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

        // List data response.
        HttpResponseMessage response = client.GetAsync(urlParameters).Result;  // Blocking call!
        if (response.IsSuccessStatusCode)
        {
            // Parse the response body. Blocking!
            var dataObjects = response.Content.ReadAsAsync<Result>().Result;
            //Console.WriteLine("{0}", dataObjects.Geo.);
        }
        else
        {
            Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
        } 
    }

UPDATE:

The Geo property is null and that's as far as I can get with this.

Preston Guillot
  • 6,493
  • 3
  • 30
  • 40
IbrarMumtaz
  • 4,235
  • 7
  • 44
  • 63

3 Answers3

5

There are two problems that I see. One is with the ordering and another is with namespace.

You need to specify the order on all of your properties and objects but you also need to specify the namespace on all of them.

    [DataContract(Name = "result", Namespace = "")]
    public class Result
    {
        [DataMember(Name = "postcode", Order = 1)]
        public string Postcode { get; set; }

        [DataMember(Name = "geo", Order = 2)]
        public Geo Geo { get; set; }
    }

Likewise on your GEO object you have to specify both for the mapping to work on the HttpClient.

    [DataContract(Name = "geo", Namespace = "")]
    public class Geo
    {
        [DataMember(Name = "lat", Order = 1)]
        public string Lat { get; set; }
    }

Hope that helps.

Louie Bacaj
  • 1,417
  • 13
  • 18
  • The namespace of `Geo` doesn't matter here because it's not the root element of the document, nor does the ordering of `lat` (at least, not yet) since it's the only element he's deserializing. – Preston Guillot Feb 11 '15 at 21:43
  • If you take them out it wont work... try it... at least the namespacing needs to be provided for sure because of the way that xml is formatted... – Louie Bacaj Feb 11 '15 at 21:44
  • Well i added in the NS and I got: Error in line 1 position 9. Expecting element 'result' from namespace 'http://schemas.datacontract.org/2004/07/RiskChecker.Lib'.. Encountered 'Element' with name 'result', namespace ''. Which make sense, as the elements are being received from the WebService without a namespace. So the Namespace can be omitted. – IbrarMumtaz Feb 11 '15 at 21:45
  • I just tried it myself and definitely fails on the namespacing missing on GEO – Louie Bacaj Feb 11 '15 at 21:45
  • I derped my last comment, I didn't realize I had added the empty namespace too. – Preston Guillot Feb 11 '15 at 21:46
  • It's a shame that you have to define the order of the XML elements to correctly parse them... This works though, correct answer – nxu Feb 11 '15 at 21:48
  • Thanks, that works now. Was the simplest thing ever! – IbrarMumtaz Feb 11 '15 at 21:52
3

This is an ordering thing, but it's one you can work around with your definition of Result. DataContractSerializer, unless told otherwise, expects elements to be in alphabetical order, not the order defined in the class. But, you can use the Order property of DataMember to explicitly tell it in what order to expect and emit elements.

Changing your definition of Result to

[DataContract(Name = "result", Namespace = "")]
public class Result
{
    [DataMember(Name = "postcode", Order = 1)]
    public string Postcode { get; set; }

    [DataMember(Name = "geo", Order = 2)]
    public Geo Geo { get; set; }
}

should fix your issue. You can refer to MSDN for a more complete description of ordering semantics in data contracts.

As per @LouieBacaj's answer, you will also need to add the empty namespace to your Geo class to correctly deserialze the nested element:

[DataContract(Name = "geo", Namespace = "")]
public class Geo
{
    [DataMember(Name = "lat")]
    public string Lat { get; set; }
}
Preston Guillot
  • 6,493
  • 3
  • 30
  • 40
2

public string Geo is supposed to be public Geo Geo, isn't it?

nxu
  • 2,202
  • 1
  • 22
  • 34
  • 1
    Also - I think the datacontract with name goes over the Geo inside the result. A shortcut for this kind of stuff is to use a tool that generates the XSD from the XML and then classes from the XSD for you. XSD.exe does this for you. – Jochen van Wylick Feb 11 '15 at 20:19
  • Thanks, made some changes but right now its not throwing exceptions but now Geo is Null. I hope this is not an ordering thing. As its a public free to use API so hopefully someone will know how to get it to work properly. – IbrarMumtaz Feb 11 '15 at 20:28
  • I don't have access to that web site, but I loaded this into a memory stream and passed it through a `DataContractSerializer` and it deserialized correctly. So maybe it's something in the way the deserializer is configured in the `HttpClient`? – Steve Mitcham Feb 11 '15 at 21:07