Search code examples
c#xmlxml-serializationdeserializationdatacontractserializer

Deserialize google geocoding XML using Datacontract in C#


I have the following Google geocoding XML

<GeocodeResponse> 
 <status>OK</status> 
 <result> 
  <type>street_address</type> 
  <formatted_address>1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA</formatted_address> 
  <address_component> 
   <long_name>1600</long_name> 
   <short_name>1600</short_name> 
   <type>street_number</type> 
  </address_component> 
  <address_component> 
   <long_name>Amphitheatre Pkwy</long_name> 
   <short_name>Amphitheatre Pkwy</short_name> 
   <type>route</type> 
  </address_component>
  ....
  <geometry> 
   <location> 
    <lat>37.4217550</lat> 
    <lng>-122.0846330</lng> 
   </location> 
   <location_type>ROOFTOP</location_type> 
   <viewport> 
    <southwest> 
     <lat>37.4188514</lat> 
     <lng>-122.0874526</lng> 
    </southwest> 
    <northeast> 
     <lat>37.4251466</lat> 
     <lng>-122.0811574</lng> 
    </northeast> 
   </viewport> 
  </geometry> 
 </result> 
</GeocodeResponse>

And the following object

[DataContract(Namespace = "")]
public class GeocodeResponse
{
    [DataMember(Name = "status", Order = 1)]
    public string Status { get; set; }

    [DataMember(Name = "result", Order = 2)]
    public List<Result> Results { get; set; }

    [DataContract(Name = "result", Namespace = "")]
    public class Result
    {
        [DataMember(Name = "geometry")]
        public CGeometry Geometry { get; set; }

        [DataContract(Name = "geometry", Namespace = "")]
        public class CGeometry
        {
            [DataMember(Name = "location")]
            public CLocation Location { get; set; }

            [DataContract(Name = "location", Namespace = "")]
            public class CLocation
            {
                [DataMember(Name = "lat", Order = 1)]
                public double Lat { get; set; }
                [DataMember(Name = "lng", Order = 2)]
                public double Lng { get; set; }
            }
        }
    }
}

I am trying to deserialize using the following method

DataContractSerializer serializer = new DataContractSerializer(typeof(GeocodeResponse));

var response = (GeocodeResponse)serializer.ReadObject(request.GetResponse().GetResponseStream());

After deserialization, Results is always empty. What am I doing wrong?

UPDATE:

Changed Result element. Getting another error now:

There was an error deserializing the object of type GeocodeResponse. The data at the root level is invalid. Line 1, position 1.

...

    [DataMember(Name = "result", Order = 2)]
    public CResult Result { get; set; }

    [DataContract]
    public class CResult
    {
    ...

I am able to Deserialize the original object using JSON like below.

DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(GeocodeResponse));

Solution

  • You can't really use the DataContractSerializer (DCS) to deserialize the response from that request, you need to use the XmlSerializer. DCS doesn't support unwrapped collections, which is what the response contains - like the one shown below.

    <a>
      <b/>
      <c/>
      <c/>
      <c/>
    </a>
    

    The DCS only supports collections when they're wrapped in their own element:

    <a>
      <b/>
      <cs>
        <c/>
        <c/>
        <c/>
      </cs>
    </a>
    

    The XmlSerializer does support such collections. The code below shows a partial class structure for deserializing a response from the Google geocoding XML.

    public class StackOverflow_7521821
    {
        [XmlRoot(ElementName = "GeocodeResponse", Namespace = "")]
        public class GeocodeResponse
        {
            [XmlElement(ElementName = "status")]
            public GeocodeResponseStatusCode Status;
            [XmlElement(ElementName = "result")]
            public List<GeocodeResponseResult> Results;
        }
        [XmlType(Namespace = "")]
        public class GeocodeResponseResult
        {
            [XmlElement(ElementName = "type")]
            public List<string> Types;
            [XmlElement(ElementName = "formatted_address")]
            public string FormattedAddress;
            [XmlElement(ElementName = "address_component")]
            public List<GeocodeResponseAddressComponent> AddressComponents;
            [XmlElement(ElementName = "geometry")]
            public GeocodeResponseResultGeometry Geometry;
        }
        [XmlType(Namespace = "")]
        public class GeocodeResponseAddressComponent
        {
            [XmlElement(ElementName = "long_name")]
            public string LongName;
            [XmlElement(ElementName = "short_name")]
            public string ShortName;
            [XmlElement(ElementName = "type")]
            public List<string> Types;
        }
        [XmlType(Namespace = "")]
        public class GeocodeResponseResultGeometry
        {
            [XmlElement(ElementName = "location")]
            public Location Location;
            [XmlElement(ElementName = "location_type")]
            public GeocodeResponseResultGeometryLocationType LocationType;
            [XmlElement(ElementName = "viewport")]
            public GeocodeResponseResultGeometryViewport Viewport;
        }
        [XmlType(Namespace = "")]
        public class GeocodeResponseResultGeometryViewport
        {
            [XmlElement(ElementName = "southwest")]
            public Location Southwest;
            [XmlElement(ElementName = "northeast")]
            public Location Northeast;
        }
        public enum GeocodeResponseStatusCode
        {
            OK, 
            ZERO_RESULTS, 
            OVER_QUERY_LIMIT, 
            REQUEST_DENIED, 
            INVALID_REQUEST,
        }
        public enum GeocodeResponseResultGeometryLocationType
        {
            ROOFTOP,
            RANGE_INTERPOLATED,
            GEOMETRIC_CENTER,
            APPROXIMATE,
        }
        [XmlType(Namespace = "")]
        public class Location
        {
            [XmlElement(ElementName = "lat")]
            public string Lat;
            [XmlElement(ElementName = "lng")]
            public string Lng;
        }
        public static void Test()
        {
            XmlSerializer xs = new XmlSerializer(typeof(GeocodeResponse));
            WebClient c = new WebClient();
            byte[] response = c.DownloadData("http://maps.googleapis.com/maps/api/geocode/xml?address=1+Microsoft+Way,+Redmond,+WA&sensor=true");
            MemoryStream ms = new MemoryStream(response);
            GeocodeResponse geocodeResponse = (GeocodeResponse)xs.Deserialize(ms);
            Console.WriteLine(geocodeResponse);
    
            c = new WebClient();
            response = c.DownloadData("http://maps.googleapis.com/maps/api/geocode/xml?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true");
            ms = new MemoryStream(response);
            geocodeResponse = (GeocodeResponse)xs.Deserialize(ms);
            Console.WriteLine(geocodeResponse);
        }
    }