The "__type"
parameter is added by DataContractJsonSerializer
to represent polymorphic type information. From the docs:
Polymorphism
Polymorphic serialization consists of the ability to serialize a derived type where its base type is expected. This is supported for JSON serialization by WCF comparable to the way XML serialization is supported. For example, you can serialize MyDerivedType where MyBaseType is expected, or serialize Int where Object is expected...
Preserving Type Information
As stated earlier, polymorphism is supported in JSON with some limitations...
To preserve type identity, when serializing complex types to JSON a "type hint" can be added, and the deserializer recognizes the hint and acts appropriately. The "type hint" is a JSON key/value pair with the key name of "__type" (two underscores followed by the word "type"). The value is a JSON string of the form "DataContractName:DataContractNamespace" (anything up to the first colon is the name).
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
and DataMember
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:
public static class Namespaces
{
public const string Pcmiler = @"http://pcmiler.alk.com/APIs/v1.0";
}
[DataContract(Namespace = Namespaces.Pcmiler)]
public class Coordinates
{
public double Lat { get; set; }
public double Lon { get; set; }
}
[KnownType(typeof(CalculateMilesReport))]
[KnownType(typeof(GeoTunnelReport))]
[DataContract(Namespace = Namespaces.Pcmiler)]
public abstract class Report
{
[DataMember]
public string RouteID { get; set; }
}
[DataContract(Namespace = Namespaces.Pcmiler)]
public class CalculateMilesReport : Report
{
[DataMember]
public double TMiles { get; set; }
}
[DataContract(Namespace = Namespaces.Pcmiler)]
public class GeoTunnelReport : Report
{
[DataMember]
public List<Coordinates> GeoTunnelPoints { get; set; }
}
Note the [KnownType(typeof(XXXReport))]
attributes attached to Report
. In order to deserialize the JSON correctly, all expected subclasses of Report
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 a List<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:
var list = DataContractJsonSerializerHelper.GetObject<List<Report>>(rawJSON);
using
public static class DataContractJsonSerializerHelper
{
private static MemoryStream GenerateStreamFromString(string value)
{
return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
}
public static T GetObject<T>(string json)
{
var serializer = new DataContractJsonSerializer(typeof(T));
using (var stream = GenerateStreamFromString(json))
{
return (T)serializer.ReadObject(stream);
}
}
}
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
svcutil.exe http://pcmiler.alk.com/APIs/REST/v1.0/service.svc?wsdl
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.