2

I have a three classes

public class A<T>
{
    public bool Success {get; set;}
    public string Reason {get; set;}
    public T Data {get; set;}
}

public class B
{ 
    public string Token {get; set;}
}

public class C
{
    public int SomeInt {get; set;}
    public string SomeString {get; set;}
    public double SomeDouble {get; set;}
}

I call my web service and pass in A or A to deserialise into like this

 async Task<T> MyTask<T>(...) (where T is A<B> or A<C>)

The service works fine and returns some JSON which if I try and to deserialise into a var, I'm getting nothing deserialised

To deserialise, I'm using

 var foo = JsonConvert.DeserializeObject<T>(response.Content);

Is there a limitation to how I can deserialise the object so I can only use a base class rather than this form of class?

Nodoid
  • 1,449
  • 3
  • 24
  • 42
  • I don't understand your last sentence which seems to be the crux of your question. – DavidG Apr 18 '18 at 15:38
  • Am I limited to only passing in A rather than A for deserialisation? – Nodoid Apr 18 '18 at 15:43
  • 1
    You need your web service call to have an extra generic param `async Task MyTask(...) where T2 : class where T1 : A` - then you can deserialize `var foo = JsonConvert.DeserializeObject(response.Content)` and call your web service `await MyTask, B>()` – Vidmantas Blazevicius Apr 18 '18 at 16:09

2 Answers2

3

Yes, you can not just pass A because A is not base class, it is generic class and does not exists on its own.

C# generates generic classes during compile time, which means if you use A<B> in your code, C# will generate something like:

public class generated_A_B
{
    public bool Success { get; set; }
    public string Reason { get; set; }
    public B Data { get; set; }
}

Nothing will be generated for A<C>, if not explicitly used. Reason is obvious, if you generate classes for every single combination of the A, you will bloat your code.

In your current situation it is better to just call them explicitly

void Main()
{
    CallAB();
    CallAC();
}

A<B> CallAB()
{
    return ServiceCall<A<B>>("/api/ab");
}

A<C> CallAC()
{
    return ServiceCall<A<C>>("/api/ac");
}

If you really want to get "generic" A, you should make A an actual base class and have your API to return the type of Data. In my example I just use Name of the type, but you probably should use FullName that includes namespace, to avoid name conflicts.

void Main()
{
    var json = @"
    {
        ""success"": true,
        ""reason"": ""All good"",
        ""type"": ""B"",
        ""data"": {
            ""token"": ""1234-5678""
        }
    }";
    var foo = JsonConvert.DeserializeObject<A>(json);
    var type = Assembly.GetExecutingAssembly().GetTypes().Where(i => i.IsClass && i.Name == foo.Type).FirstOrDefault();
    if (type == null)
    {
        throw new InvalidOperationException(string.Format("Type {0} not found", foo.Type));
    }
    var data = foo.Data.ToObject(type);
}

public class A
{
    public bool Success { get; set; }
    public string Reason { get; set; }
    public string Type { get; set; }
    public JToken Data { get; set; }
}

public class B
{
    public string Token { get; set; }
}
0

I would recommend either to go for the easy solution: create an intermediate merged your classes B and C, to something like

    public class BC
    {
        public string Token { get; set; }
        public int SomeInt { get; set; }
        public string SomeString { get; set; }
        public double SomeDouble { get; set; }

        public bool IsB => !String.IsNullOrEmpty(Token);

        public B ToB() => new B() { Token = Token };

        public C ToC() => new C() { SomeInt = SomeInt, SomeString = SomeString, SomeDouble = SomeDouble };
    }

Then you can call your service, and convert BC either to B or C:

async Task<A<BC>> MyTask<A<BC>>(...)
var abc = await MyTask(...);
if(abc.Data.IsB)
{
    var resB = abc.data.ToB();
}
else
{
    var resC = abc.data.ToC();
}

Or go to the more complicated solution, with some JsonConverter as explained in this answer:

how to implement custom jsonconverter in json net to deserialize a list of base

Jean
  • 4,911
  • 3
  • 29
  • 50