I am trying to write a generic method for deserializing from json file(s) with Json.NET
I want to be able to support deserializing files that contain objects and also arrays of objects. Below are simplified versions of the two generic methods I have at the moment:
/// <summary> Deserializes object or an array of objects from a list of files. </summary>
public static List<T> Deserialize<T>(List<string> filePathsList)
{
var result = new List<T>();
foreach (var file in filePathsList)
// HOW DO I FIND OUT IF T IS A LIST HERE?
// Resharper says: the given expression is never of the provided type
if (typeof(T) is List<object>)
{
var deserialized = Deserialize<List<T>>(file);
result.AddRange(deserialized);
}
// Resharper says: Suspicious type check: there is not type in the solution
// which is inherited from both 'System.Type' and 'System.Collections.IList'
else if (typeof(T) is IList)
{
// deserializing json file with an array of objects
var deserialized = Deserialize<List<T>>(file);
result.AddRange(deserialized);
}
else
{
// deserializing a single object json file
var deserialized = Deserialize<T>(file);
result.Add(deserialized);
}
return result;
}
/// <summary> Deserializes object or an array of objects from a file. </summary>
public static T Deserialize<T>(string filepath)
{
return JsonConvert.DeserializeObject<T>(File.ReadAllText(filepath));
}
The problem is, I need to know if the T is an object or a List of objects, before deserializing. How do I check for that inside the generic method? Because resharper gives me warnings and I can't seem to figure this out.
Edit: I made a mistake in the example code, as @Medo42 points out, it would call Deserialize() with a List<List<Something>>
Also, I should not have included an example involving the json serialization at all, it should have been more generic. The question is about finding out if T is a list of objects.
Thanks in advance for any help.
This is not a complete answer, but it's too long for a comment and might help you understand some of the issues better.
// Resharper says: the given expression is never of the provided type
if (typeof(T) is List<object>)
And Resharper is right. The is
operator checks whether the instance on the left is of the type on the right, so in your case it checks if typeof(T)
is an instance of List<object>
. However, typeof(T)
returns a Type
instance which represents the type of T
. The correct way to check (if you are after the exact type) would be
if (typeof(T) == typeof(List<object>))
But note that this will only apply if T
is exactly List<object>
. If it is also OK to have a subtype of List<object>
, the line would be
if (typeof(List<object>).IsAssignableFrom(typeof(T)))
But your problems don't end there. You seem to assume that List<object>
is a supertype of all lists. This is not the case, even if we can assume that we will only ever work with the List<T>
implementation for lists. The reson for this is that List<T>
is invariant.
Invariance means that a list of cats is not a list of mammals. If this seems counterintuitive, that's because you think of a list as a fixed collection that you want to read from. However, you can also add new items to a C# list, and if you were allowed to treat a List<Cat>
as a List<Mammal>
you could end up trying to add an elephant to that list, and this would cause no end of confusion to anyone else still holding a reference to that list as a List<Cat>
.
For a solution to the type checking problem, I think drf's comment to dotctor's answer is the cleanest way to do what you think you want to do:
typeof(T).GetGenericTypeDefinition() == typeof(List<>)
As a final aside, the following code also looks wonky:
var deserialized = Deserialize<List<T>>(file);
You do this after figuring out that T
is really a List<Something>
, so you're now trying to deserialize your file as a List<List<Something>>
, which is probably not what you want.
You can check it easily
if (typeof(T).Name == "List`1")
{
// T is a generic list
}
As mentioned by drf a better way which does not rely on internal implementations is:
if (typeof(T).GetGenericTypeDefinition() == typeof(List<>))
{
// T is a generic list
}
You dont need this, you can simplify your code just using LINQ!!!
/// <summary>
/// Deserializes object or an array of objects from a list of files.
/// </summary>
public static List<T> Deserialize<T>(List<string> filePathsList)
{
return filePathsList
.Select(System.IO.File.ReadAllText)
.Select(JsonConvert.DeserializeObject<T>)
.ToList();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With