2010-11-06 20 views
9

En resumen, estoy tratando de deserializar una respuesta JSON de la API de Bing Maps geocodificación REST,Problema con deserializar JSON en DataMember "__type"

creé mi clase de respuesta, y ahora, cuando estoy tratando de de hecho, deserializo una respuesta, obtengo el siguiente error:

No se espera el tipo '{0}' con el nombre del contrato de datos '{1}: {2}'. Considere el uso de un DataContractResolver o agregue cualquier tipo no conocido de forma estática a la lista de tipos conocidos, por ejemplo, utilizando el atributo KnownTypeAttribute o agregándolos a la lista de tipos conocidos pasados ​​a DataContractSerializer.

que está tratando de deserializar esta línea de JSON, y falla:

"__type": "Location:http:\/\/schemas.microsoft.com\/search\/local\/ws\/rest\/v1", 

Mi clase de respuesta se parece a esto

 [DataContract] 
     public class GeoResponse 
     { 
      [DataMember(Name = "statusDescription")] 
      public string StatusDescription { get; set; } 
      [DataMember(Name = "statusCode")] 
      public string StatusCode { get; set; } 
      [DataMember(Name = "resourceSets")] 
      public ResourceSet[] resourceSets { get; set; } 

      [DataContract] 
      public class ResourceSet 
      { 


       [DataMember(Name = "__type", IsRequired=false)] 
       public string type { get; set; } 

       [DataMember(Name = "estimatedTotal")] 
       public string EstimatedTotal { get; set; } 

       [DataMember(Name = "resources")] 
       public List<Resources> resources { get; set; } 

       [DataContract] 
       public class Resources 
       { 
        [DataMember(Name = "name")] 
        public string Name { get; set; } 

        [DataMember(Name = "point")] 
        public Point point { get; set; } 

        [DataContract] 
        public class Point 
        { 
         [DataMember(Name = "type")] 
         public string Type { get; set; } 

         [DataMember(Name = "coordinates")] 
         public string[] Coordinates { get; set; } 
        } 

        [DataMember(Name = "address")] 
        public Address address { get; set; } 

        [DataContract] 
        public class Address 
        { 
         [DataMember(Name = "addressLine")] 
         public string AddressLine { get; set; } 

         [DataMember(Name = "countryRegion")] 
         public string CountryRegion { get; set; } 

         [DataMember(Name = "formattedAddress")] 
         public string FormattedAddress { get; set; } 

         [DataMember(Name = "locality")] 
         public string Locality { get; set; } 

         [DataMember(Name = "postalCode")] 
         public string PostalCode { get; set; } 
        } 

        [DataMember(Name = "confidence")] 
        public string Confidence { get; set; } 

        [DataMember(Name = "entityType")] 
        public string EntityType { get; set; } 
       } 

      } 
     } 

    } 

Mi método que estoy usando para deserializar mi respuesta JSON:

private static GeoResponse CallGeoWS(string address) 
{ 
    string url = string.Format(
      "http://dev.virtualearth.net/REST/v1/Locations?q={0}&key={1}", 
      HttpUtility.UrlEncode(address), bingkey 
      ); 
    var request = (HttpWebRequest)HttpWebRequest.Create(url); 
    request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate"); 
    request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; 
    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(GeoResponse));    
    var res = (GeoResponse)serializer.ReadObject(request.GetResponse().GetResponseStream()); 
    return res; 
} 

Respuesta

20

En primer lugar, tenga en cuenta que el método que está citando (http://dev.virtualearth.net/REST/v1/Locations?q=Wiertzstraat+43+1047+Brussel&key=BingMapsKey), genera una respuesta diferente en comparación con la que está tratando de asignar con su clase DataContract. La respuesta se describe aquí: http://msdn.microsoft.com/en-us/library/ff701711.aspx

que han creado una DataContract para que la respuesta:

[DataContract] 
public class LocationQueryResponse 
{ 
    [DataMember] 
    public string authenticationResultCode { get; set; } 
    [DataMember] 
    public string brandLogoUri { get; set; } 
    [DataMember] 
    public string copyright { get; set; } 
    [DataMember] 
    public string statusCode { get; set; } 
    [DataMember] 
    public string statusDescription { get; set; } 
    [DataMember] 
    public string traceId { get; set; } 

    [DataMember] 
    public ResourceSet[] resourceSets { get; set; } 

    [DataContract] 
    public class ResourceSet 
    { 
     [DataMember] 
     public int estimatedTotal { get; set; } 

     [DataMember] 
     public Resource[] resources { get; set; } 

     [DataContract(Namespace = "http://schemas.microsoft.com/search/local/ws/rest/v1", Name="Location")] 
     public class Resource 
     { 
      [DataMember] 
      public string __type { get; set; } 

      [DataMember] 
      public double[] bbox { get; set; } 

      [DataMember] 
      public string name { get; set; } 

      [DataMember] 
      public Point point { get; set; } 

      [DataContract] 
      public class Point 
      { 
       [DataMember] 
       public string type { get; set; } 

       [DataMember] 
       public string[] coordinates { get; set; } 
      } 

      [DataMember] 
      public Address address { get; set; } 

      [DataContract] 
      public class Address 
      { 
       [DataMember] 
       public string addressLine { get; set; } 
       [DataMember] 
       public string adminDistrict { get; set; } 
       [DataMember] 
       public string adminDistrict2 { get; set; } 
       [DataMember] 
       public string countryRegion { get; set; } 
       [DataMember] 
       public string formattedAddress { get; set; } 
       [DataMember] 
       public string locality { get; set; } 
       [DataMember] 
       public string postalCode { get; set; } 
      } 

      [DataMember] 
      public string confidence { get; set; } 

      [DataMember] 
      public string entityType { get; set; } 
     } 

    } 
} 

En un primer momento, incluso si he creado un DataContract correcta, no funcionó y generó la misma excepción que presentado. Después de algunas investigaciones, encontré que el campo "__type" tiene un significado especial para DataContractJsonSerializer, que denota el tipo al que se debe deserializar el objeto. Para hacer esto, agregué los atributos de Nombre y Espacio de nombres al atributo DataContract de la clase Resource (verifique el código anterior).

Tengo bastante experiencia con WCF y JSON y nunca he tenido este problema antes. Parece ser bastante oscuro y el campo de tipo __type no parece ser compatible con el estándar, sino más bien una característica específica de Microsoft. Bastante molesto es el hecho de que el campo de tipo __type solo parece en algunas situaciones específicas. Por ejemplo, si en el documento JSON tiene espacio en blanco, el deserializador lo ignoraría y no lanzaría ninguna excepción. Tenía un espacio en blanco en los documentos que inicialmente utilicé para probar y por eso no obtuve errores en ese momento.

Espero que este finalmente haya sido de ayuda. :)

+0

Funcionó como un amuleto, gracias una tonelada, el espacio de nombres realmente lo solucionó. Parece algo único de Microsoft, porque he hecho lo mismo con Google, y Yahoo! geocodificación API y ¡todo funciona! ¡Gracias! – Entice

+0

¡De nada! Me alegro de poder ayudar. –

+0

útil para mí también ... ¡un millón de gracias! –

Cuestiones relacionadas