Проблема с десериализацией JSON в датамбере "__type" - PullRequest
9 голосов
/ 06 ноября 2010

Короче говоря, я пытаюсь десериализовать ответ JSON от REST API геокодирования Bing Maps,

Я создал свой класс ответа, и теперь, когда я пытаюсь фактически десериализовать ответ, яполучаю следующую ошибку:

Тип '{0}' с именем контракта данных '{1}: {2}' не ожидается.Рассмотрите возможность использования DataContractResolver или добавьте любые типы, которые не известны статически, в список известных типов - например, с помощью атрибута KnownTypeAttribute или добавив их в список известных типов, передаваемых в DataContractSerializer.

он пытается десериализоватьэта строка JSON завершается ошибкой:

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

Мой класс ответа выглядит следующим образом

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

            }
        }

    }

Мой метод, который я использую для десериализации моего ответа 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;
}

1 Ответ

20 голосов
/ 08 ноября 2010

Прежде всего, обратите внимание, что метод, который вы цитируете (http://dev.virtualearth.net/REST/v1/Locations?q=Wiertzstraat+43+1047+Brussel&key=BingMapsKey), генерирует другой ответ по сравнению с тем, который вы пытаетесь отобразить с помощью класса DataContract. Ответ описан здесь: http://msdn.microsoft.com/en-us/library/ff701711.aspx

Я создал DataContract для этого ответа:

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

    }
}

Сначала, даже если я создал правильный DataContract, он не работал и генерировал то же исключение, что и вы. После некоторых исследований я обнаружил, что поле «__type» имеет особое значение для DataContractJsonSerializer, определяя тип, к которому должен быть десериализован объект. Для этого я добавил атрибуты Name и Namespace в атрибут DataContract класса Resource (см. Код выше).

У меня довольно большой опыт работы с WCF и JSON, и я никогда раньше не сталкивался с этой проблемой. Кажется, он довольно неясен, и поле __type не соответствует стандарту, а является специфической функцией Microsoft. Весьма раздражает тот факт, что поле __type кажется только в некоторых конкретных ситуациях. Например, если в документе JSON перед ним есть пробел, десериализатор игнорирует его и не выдает никаких исключений. У меня был такой пробел в документах, которые я первоначально использовал для тестирования, и именно поэтому я не получил ошибок в этот момент.

Надеюсь, этот наконец помог. :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...