Thank you to those that offered some help, but I eventually came up with the answer I was looking for that would deserialize my object into a proper type offered via generics.
Here is my MyCustomResponse object
public class MyCustomResponse
{
[JsonProperty(PropertyName = "data")]
public object Data { get; set; }
[JsonProperty(PropertyName = "message")]
public string Message { get; set; }
[JsonProperty(PropertyName = "result")]
public string Result { get; set; }
}
The custom JsonConverter ended up like so, I look for the property in the json string "data" then convert it to an object of type T
public class MyCustomResponseConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(MyCustomResponse));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object instance = objectType.GetConstructor(Type.EmptyTypes).Invoke(null);
PropertyInfo[] props = objectType.GetProperties();
JObject jo = JObject.Load(reader);
foreach ( JProperty jp in jo.Properties() )
{
PropertyInfo prop = props.FirstOrDefault(pi =>
pi.CanWrite && string.Equals(pi.Name, jp.Name, StringComparison.OrdinalIgnoreCase));
if ( prop != null )
{
// Convert data object to what was passed in at T
if ( jp.Name == "data" )
prop.SetValue(instance, jo.SelectToken("data").ToObject(typeof(T)));
else
prop.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
}
}
return instance;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the above I created a Generic method that looks like the following and allows me to pass the command to run, extra query string and also the Type to convert 'Data' object to:
private async Task<T> GenericApiRequestAsync<T>(string command, string query)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Uri uri = new Uri(string.Format("{0}/api/{1}/?cmd={2}{3}", apiUrl, apiKey, command, query));
try {
HttpResponseMessage response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
// Convert responseContent via MyCustomResponseConverter
var myCustomResponse =
await Task.Factory.StartNew(() =>
JsonConvert.DeserializeObject<MyCustomResponse(
responseContent,
new MyCustomResponseConverter<T>()
));
return (T)myCustomResponse.Data;
}
catch(Exception ex)
{
...
}
}
Then to use the actual GenericApiRequestAsync method i simply pass it the command, query and type for the Data object to be converted into, whatever it maybe.
public async Task<Person> GetPersonAsync(int id)
{
return await GenericApiRequestAsync<Person>("person.byid", string.Format("&id={0}", id));
}
public async Task<IDictionary<string, ObjectType2>> GetObjectType2ListAsync(string name)
{
return await GenericApiRequestAsync<IDictionary<string, ObjectType2>>("show.byname", string.Format("&name={0}", name));
}
Ended up a simple solution but complex in getting there. It removes the need to process the data object a second time into in the final object as well.
Hope this solution can help others out there that come across similar JSON structures, if anyone sees a simpler way to implement the converter, i'm happy to accept any input.
Cheers