I'm using web service to get data about route mileage. Then I'm using deserializer to parse it out. Here is how JSON looks:
[{"__type":"CalculateMilesReport:http:\/\/pcmiler.alk.com\/APIs\/v1.0","RouteID":null,"TMiles":445.5]
With this response I had couple issues. Why is is wrapped into collection and how do I set object model? Also it was complaining about special __type attribute. So, I did "hack" and "prepped" string:
// Cut off first and last charachters [] - they send objects as arrays
rawJSON = rawJSON.Substring(1, rawJSON.Length - 2);
// Hide "__type" attribute as it messes up serializer with namespace
rawJSON = rawJSON.Replace("__type", "type");
Then everything worked with this object:
[DataContract]
public class PCMilerResponse
{
[DataMember(Name = "Errors", EmitDefaultValue = false)]
public PCMilerError[] Errors { get; set; }
[DataMember(Name = "TMiles", EmitDefaultValue = false)]
public decimal DrivingDistance { get; set; }
}
Now I modified call to web service and I get following response
[
{"__type":"CalculateMilesReport:http:\/\/pcmiler.alk.com\/APIs\/v1.0","RouteID":null,"TMiles":445.5},
{"__type":"GeoTunnelReport:http:\/\/pcmiler.alk.com\/APIs\/v1.0","RouteID":null,"GeoTunnelPoints":
[{"Lat":"34.730466","Lon":"-92.247147"},{"Lat":"34.704863","Lon":"-92.29329"},{"Lat":"34.676312","Lon":"-92.364654"},{"Lat":"29.664271","Lon":"-95.236735"}]
}
]
Now it makes sense why there is array and "__type". But I'm not sure how to write object to properly parse it. I guess special attributes need to be applied and maybe generic array need to be there? Any help on how to properly deserialize it?
P.S. I can do more hacking and replace those strings making it object with 2 objects inside, but I wonder if there is "proper" way to handle it.
About making the
__type
attribute disappear there are discussions on SO.Here is one, which solved in the following way:
change the WebMethod return type to object, i.e.
instead of
Another one solved by
Adding the namespace parameter
[DataContract(Namespace = "")]
to the data contract.Based on the response you can build classes in which your JSON is going to fit, but since you have the model classes you are supposed to use the same from which your JSON was built. Maybe I didn't get here something correctly from your question.
Here is a crafted model example in which your JSON would fit in:
Example usage:
The
"__type"
parameter is added byDataContractJsonSerializer
to represent polymorphic type information. From the docs:In order to use this mechanism to (de)serialize a polymorphic type, all possible derived types must be specified up front to
DataContractJsonSerializer
. See Data Contract Known Types for a discussion of how to do this.Thus, it looks like your web service is returning an array of polymorphic types. How to handle this?
The Manual Solution
One possible solution to your problem is to manually create a c# class hierarchy corresponding to the data contact hierarchy, properly annotated with
DataContract
andDataMember
attributes. Then you can leverage the "type hint" functionality of the data contract serializers to cause the correct subclass to be created automatically during deserialization. Courtesy of google, the classes you are seeing look to be documented at PC*MILER Web Services API: Report Class. Using this documentation, your classes should look like:Note the
[KnownType(typeof(XXXReport))]
attributes attached toReport
. In order to deserialize the JSON correctly, all expected subclasses ofReport
must appear as known types. According to the documentation there are 11 possible subclasses, so you will need to provide classes for all of them that you might receive from your web service.Now you can deserialize your
rawJSON
as aList<Report>
, and everything in your sample JSON should read in correctly, because you have correctly matched the data contract names, namespaces, and type hierarchies to that of the web service:using
However, that web service looks rather elaborate. Manually recreating all its classes would be tiresome.
The Automatic Solution
Since it appears your web service is a WCF service, hopefully they have published its Service Metadata. If they have, it will allow you to generate a client automatically using Add Service Reference in Visual Studio. For instructions on how to do this, see How to: Create a Windows Communication Foundation Client and How to: Add, Update, or Remove a Service Reference.
Again courtesy of google, it appears your service does provide its metadata, at http://pcmiler.alk.com/APIs/REST/v1.0/service.svc?wsdl. Doing
Seems to generate a plausible set of client classes consistent with the manual classes created above. However, you should doublecheck the documentation from your web service to ensure this the correct way to consume their service metadata.
Once a client has been created, you can access the web service as if you were calling a local c# API. See Accessing Services Using a WCF Client for how. The article Creating and Consuming Your First WCF Service gives an overview of the entire process.