Click here to Skip to main content
15,881,172 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi all,

This is a follow on from the following question I asked yesterday:
Return complex objects with Web API[^]
Thanks very much to KornfeldEliyahuPeter[^] for his help there.

I'm now creating a console application to access the Web Api. I have used Calling-a-web-api-from-a-net-client[^] from Microsoft as my template for creating my client but I'm getting an issue when it tries to de-serialise my object.

To save having to jump between questions here is my object structure:

C#
public interface IMyFileInterface
{
    string FileName {get;set;}
    MyEnum FileType {get;set;
    DateTime UploadDate {get;set;}
}

Classes:
C#
[DataContract]
[KnownType(typeof(FooFileModel))]
public class ReturnModel
{
   [DataMember]
   public string Name {get;set;}
   [DataMember]
   public List<IMyFileInterface> LatestFiles {get;set;}
}

[DataContract]
public class FooFileModel : IMyFileInterface
{
    [DataMember]
    string FileName {get;set;}
    [DataMember]
    MyEnum FileType {get;set;
    [DataMember]
    DateTime UploadDate {get;set;}

}


My api call is to an address similar to: "http://localhost:12345/API/API/GetReport"

My client code looks like this:

C#
private async Task<UtilitiesReportModel> GetReportModalAsync()
{
   using (var client = new HttpClient())
   {
          client.BaseAddress = new Uri("http://localhost:12345/");
          client.DefaultRequestHeaders.Accept.Clear();
          client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                Task<HttpResponseMessage> response =  client.GetAsync("API/API/GetReport");
                if (response.Result.IsSuccessStatusCode)
                {
                    UtilitiesReportModel reportModel = await response.Result.Content.ReadAsAsync<UtilitiesReportModel>();
                    Debug.WriteLine(reportModel.Name);
                    Debug.WriteLine(reportModel.LatestUploads.Count);
                    return reportModel;
                }
                return null;

            }
        }


Which is called from my "main" method as follows:

C#
var res = GetReportModalAsync();
res.Wait();
var theRes = res.Result;


However, when res.Wait() triggers I get the following error:

Could not create an instance of type Common.Objects.IMyFileInterface. Type is an interface or abstract class and cannot be instantiated. Path 'LatestUploads[0].SurveyType', line 1, position 220.

(I don't know if it is relevant but SurveyType is a custom enum).

Now I understand what the error says, for some reason when it tries to de-serialise it is trying to create an instance of an interface, as we all know this is impossible. How do I tell it to create an instance of it's original class? When I look at the response from the API call it has the original data type in.

Any help would be appreciated as I would like to be able to code against interfaces and not hard code the classes if I can help it.

When I call the api in a web browser it displays the result in the following XML. (I know the comment has asked for it in JSon).

XML
<ReportModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Common.Objects">
    <CurrentMonth i:nil="true"/>
    <LastRefresh>0001-01-01T00:00:00</LastRefresh>
    <LatestUploads xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <d2p1:anyType i:type="FooFileModel">
            <DateTimeUploaded>2014-12-08T14:23:37.243</DateTimeUploaded>
            <FileName>05122014 cleaned for loading.xlsx</FileName>
            <NumberOfEntries>76</NumberOfEntries>
            <SurveyType>TypeA</SurveyType>
        </d2p1:anyType>
        <d2p1:anyType i:type="FooFileModel">
            <DateTimeUploaded>2014-12-05T12:04:47.877</DateTimeUploaded>
            <FileName>04122014 Cleaned for loading.xlsx</FileName>
            <NumberOfEntries>5</NumberOfEntries>
            <SurveyType>TypeB</SurveyType>
        </d2p1:anyType>
        <d2p1:anyType i:type="FooFileModel">
            <DateTimeUploaded>2014-12-05T12:05:28.14</DateTimeUploaded>
            <FileName>04122014 Cleaned for loading.xlsx</FileName>
            <NumberOfEntries>6</NumberOfEntries>
            <SurveyType>TypeC</SurveyType>
        </d2p1:anyType>
    </LatestUploads>
    <Name>Site1</Name>
</ReportModel>


If I call "response.Result.Content.ReadAsStringAsync();

I get the following out, which I believe is the Json response:

{"$type":"Common.Objects.ReportModel, Common",
"Name":"Site1",
"LatestUploads":[
    {"$type":"Common.Objects.FooFileModel, Common",
     "SurveyType":2,
     "FileName":"08122014 Cleaned for loading.xlsx",
     "DateTimeUploaded":"2014-12-09T14:02:36.147",
     "NumberOfEntries":69},
    {"$type":"Common.Objects.FooFileModel, Common",
     "SurveyType":1,
     "FileName":"08122014 Cleaned for loading.xlsx",
     "DateTimeUploaded":"2014-12-09T14:03:41.11",
     "NumberOfEntries":11},
    {"$type":"Common.Objects.FooFileModel, Common",
     "SurveyType":2,
     "FileName":"05122014 cleaned for loading.xlsx",
     "DateTimeUploaded":"2014-12-08T14:23:37.243",
     "NumberOfEntries":76}],
"CurrentMonth":null,"LastRefresh":"0001-01-01T00:00:00"}
Posted
Updated 9-Dec-14 4:22am
v3
Comments
Zoltán Zörgő 9-Dec-14 10:05am    
What is the exact json you get back?
Pheonyx 9-Dec-14 10:11am    
I know you've asked for the JSON, but I've added the XML view I see when I call the api in a web browser. Is that any help or do you need to see the JSON specifically?
Pheonyx 9-Dec-14 10:23am    
I've also added the JSon response as well.
Kornfeld Eliyahu Peter 9-Dec-14 12:42pm    
The XML/JSON response do not fit the DataContract definition at the top of your question...Why?
Kornfeld Eliyahu Peter 9-Dec-14 15:50pm    
To be honest I can't reason it for you (I just have no tim to investigate), but if you switch from application/json to application/xml it will work...

After all the investigation it is simple...
When you serialize you object to JSON you use a specific format (that means you do not use the default serializer for JSON - a fact you should have share with us!) that includes type information...But when you deserialize it on the other side you use an other (default) library that knows nothing about that funny $type directive so it will try to deserialize it based on the provided type, which have an interface inside it, that blows anything away...
You have two options:
1. Use XML - XML can handle type information with no extra effort from your side...
2. Use ReadAsStringAsync to get your JSON as a string, than deserialize it 'manualy' using the same library you used for serialization...
 
Share this answer
 
I have asked for json as your code is asking for json (in the accept header). And because json is more expressive. But as I know, you can't have interfaces as DataContract - because of obvious reason of ambiguity. Check here: http://geekswithblogs.net/mnf/archive/2012/08/11/using-interfaces-in-datacontracts-for-wcf-service-versioning.aspx[^]

But this approach can help you: https://coab.wordpress.com/2010/03/01/serializing-and-deserializing-derived-types-or-interfaces-in-wcf/[^]
 
Share this answer
 
Comments
Kornfeld Eliyahu Peter 9-Dec-14 13:23pm    
But interface does not signed as DataContract!
Zoltán Zörgő 9-Dec-14 13:27pm    
True, but you have a generic list of interface. Try using an abstract class as base and KnownType attribute. Currently you have put KnownType on a class that is using it, but not related to it (that should work with classes). This is a good article about KnownType: http://www.codeproject.com/Tips/601224/What-is-KnownType-Attribute-and
Kornfeld Eliyahu Peter 9-Dec-14 13:32pm    
There is a KnownType attribute and it is clear from the JSON that that type used when serializing? (In fact it is impossible to serialize an interface and without KnownType it had failed)
Zoltán Zörgő 9-Dec-14 13:36pm    
Check my edit. When you serialize, everything is clear. The runtime knows who is who. In inverse scenario, it is not so clear. Try with class hierarchy instead of interface, and put knowntype on the base class.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900