2010-11-06 5 views
9

En bref, je suis en train de désérialiser une réponse JSON de l'API Bing Maps géocodage REST,problème avec désérialisation JSON sur DataMember « __type »

J'ai créé ma classe de réponse, et maintenant quand je suis en train de En fait désérialiser une réponse, j'obtiens l'erreur suivante:

Le type '{0}' avec le nom de contrat de données '{1}: {2}' n'est pas prévu. Envisagez d'utiliser DataContractResolver ou ajoutez des types non connus de façon statique à la liste des types connus, par exemple, en utilisant l'attribut KnownTypeAttribute ou en les ajoutant à la liste des types connus transmis à DataContractSerializer.

il essaie de désérialiser cette ligne de JSON et échoue:

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

Ma classe de réponse ressemble à ce

 [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; } 
       } 

      } 
     } 

    } 

Ma méthode j'utilise pour désérialiser ma réponse 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; 
} 

Répondre

20

Tout d'abord, veuillez noter que la méthode que vous citez (http://dev.virtualearth.net/REST/v1/Locations?q=Wiertzstraat+43+1047+Brussel&key=BingMapsKey), génère une réponse différente de celle que vous essayez de mapper avec votre classe DataContract. La réponse est décrite ici: http://msdn.microsoft.com/en-us/library/ff701711.aspx

J'ai créé un DataContract pour cette réponse:

[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; } 
     } 

    } 
} 

Au début, même si je créé un DataContract correct, il ne fonctionnait pas et il a généré la même exception que vous présenté. Après quelques recherches, j'ai trouvé que le champ "__type" avait une signification particulière pour DataContractJsonSerializer, indiquant le type auquel l'objet devrait être désérialisé. Afin de faire ce travail, j'ai ajouté des attributs Name et Namespace à l'attribut DataContract de la classe Resource (veuillez vérifier le code ci-dessus).

J'ai une certaine expérience avec WCF et JSON et jamais rencontré ce problème avant. Il semble être assez obscur et le champ __type ne semble pas être conforme à la norme, mais plutôt une fonctionnalité spécifique à Microsoft. Assez ennuyeux est le fait que le champ __type ne semble que dans certaines situations spécifiques. Par exemple, si dans le document JSON vous avez un espace blanc avant, le désérialiseur l'ignorera et ne lancera aucune exception. J'avais un tel espace dans les documents que j'avais initialement utilisés pour les tests et c'est pourquoi je n'ai pas eu d'erreurs à ce moment-là.

J'espère que celui-ci a finalement aidé. :)

+0

Cela a fonctionné comme un charme, merci une tonne, l'espace de noms l'a effectivement fixé. On dirait quelque chose d'unique à Microsoft en effet, parce que j'ai fait la même chose avec Google, et Yahoo! géocodage API et là tout fonctionne! Merci! – Entice

+0

De rien! Heureux d'avoir pu aider. –

+0

utile pour moi aussi ... un million de mercis! –

Questions connexes