I want a generic function to convert any class instance to a dict so i can easily work with it in a generic fashion. In my case i want to convert any instance SSF.Mod to a dict that i can iterate over to dynamically generate part of the UI based on it.
I thought about using json.net but upon quick inspection i have seen noone ever asking for the excact same thing.
Cut down version of SSF.Mod class:
namespace SSF
{
public class FileWithHash
{
public string PrivatePath { get; set; }
public FileWithHash(string sharedPath, string hash)
{
PrivatePath = sharedPath; Hash = hash;
}
}
public class Mod
{
[JsonIgnore]
private Game Game { get; set; }
public bool Disabled {
get { ... }
}
public string Id { get; set; }
public DirectoryInfo Directory { get; set; }
public FileWithHash GroFile { get; set; }
public FileWithHash Thumbnail { get; set; }
public Publishedfiledetail Details { get; set; }
public List<string> Tags { get {
if (Details is null) return null;
var ret = new List<string>();
foreach (var tag in Details.tags) {
ret.Add(tag.tag);
}
return ret;
} }
}
}
Cut down example instace as json:
{
"Disabled": false,
"Id": "1805661862",
"Directory": {
"OriginalPath": "S:\\Steam\\steamapps\\workshop\\content\\564310\\1805661862",
"FullPath": "S:\\Steam\\steamapps\\workshop\\content\\564310\\1805661862"
},
"WorkshopVersion": "WorkshopVersion_01",
"GroFile": {
"PrivatePath": "Gro_File/DeepValley.gro",
"Hash": "B68F60F4C87077DAF59F684B0306963BA6D4B351"
},
"Details": {
"publishedfileid": "1805661862",
"result": 1,
"creator": "76561198016985053",
"creator_app_id": 564380,
"consumer_app_id": 564310,
"filename": "",
}
I thought about something like
Dictionary<string, object> dictOfMod = instanceOfMod.toDict();
foreach(var entry in dictOfMod){
if (entry is bool) etc...
Convert an instance to a Dictionary:
Mod mod = new Mod();
var modDictionary = mod.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
.ToDictionary(
propertyInfo => propertyInfo.Name,
propertyInfo => propertyInfo.GetValue(mod));
The following method converts a type and its complete declaration type tree to a Dictionary<string, object>
The default Key of the Dictionary<string, object> is the property name.
Collections are converted to Dictionary<string, object> where the Key is the item's index.
The last item of such a dictionary is the count where the Key is "Count".
To determine if a Dictionary<string, object> is a collection use the Key "IsCollection" which returns a boolean value.
You can use this extension method:
Extensions.cs
public static class Extensions
{
public static Dictionary<string, object> ToDictionary(this object instanceToConvert)
{
Dictionary<string, object> resultDictionary = instanceToConvert.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
.Where(propertyInfo => !propertyInfo.GetIndexParameters().Any())
.ToDictionary(
propertyInfo => propertyInfo.Name,
propertyInfo => Extensions.ConvertPropertyToDictionary(propertyInfo, instanceToConvert));
resultDictionary.Add("IsCollection", false);
return resultDictionary;
}
private static object ConvertPropertyToDictionary(PropertyInfo propertyInfo, object owner)
{
Type propertyType = propertyInfo.PropertyType;
object propertyValue = propertyInfo.GetValue(owner);
if (propertyValue is Type)
{
return propertyValue;
}
// If property is a collection don't traverse collection properties but the items instead
if (!propertyType.Equals(typeof(string)) && typeof(IEnumerable).IsAssignableFrom(propertyType))
{
var items = new Dictionary<string, object>();
var enumerable = propertyInfo.GetValue(owner) as IEnumerable;
int index = 0;
foreach (object item in enumerable)
{
// If property is a string stop traversal
if (item.GetType().IsPrimitive || item is string)
{
items.Add(index.ToString(), item);
}
else if (item is IEnumerable enumerableItem)
{
items.Add(index.ToString(), ConvertIEnumerableToDictionary(enumerableItem));
}
else
{
Dictionary<string, object> dictionary = item.ToDictionary();
items.Add(index.ToString(), dictionary);
}
index++;
}
items.Add("IsCollection", true);
items.Add("Count", index);
return items;
}
// If property is a string stop traversal
if (propertyType.IsPrimitive || propertyType.Equals(typeof(string)))
{
return propertyValue;
}
PropertyInfo[] properties =
propertyType.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
if (properties.Any())
{
Dictionary<string, object> resultDictionary = properties.ToDictionary(
subtypePropertyInfo => subtypePropertyInfo.Name,
subtypePropertyInfo => propertyValue == null
? null
: (object) Extensions.ConvertPropertyToDictionary(subtypePropertyInfo, propertyValue));
resultDictionary.Add("IsCollection", false);
return resultDictionary;
}
return propertyValue;
}
private static Dictionary<string, object> ConvertIEnumerableToDictionary(IEnumerable enumerable)
{
var items = new Dictionary<string, object>();
int index = 0;
foreach (object item in enumerable)
{
// If property is a string stop traversal
if (item.GetType().IsPrimitive || item is string)
{
items.Add(index.ToString(), item);
}
else
{
Dictionary<string, object> dictionary = item.ToDictionary();
items.Add(index.ToString(), dictionary);
}
index++;
}
items.Add("IsCollection", true);
items.Add("Count", index);
return items;
}
}
// Test object definition
class TestClass
{
public TestClass()
{
this.TheNestedList = new List<List<double>>() {new List<double>() {1, 2, 3, 4}, new List<double>() {11, 22, 33, 44}};
}
public List<List<double>> TheNestedList { get; set; }
}
// Usage example
static void Main(string[] args)
{
var testClass = new TestClass();
// Convert testClass instance to Dictionary<string, object>
Dictionary<string, object> testClassDictionary = testClass.ToDictionary();
// Consume the result and
// retrieve the outer List<List<double>>
var nestedListProperty = testClassDictionary["NestedList"] as Dictionary<string, object>;
if ((bool) nestedListProperty["IsCollection"])
{
// Retrieve the inner List<double>
for (var index = 0; index < (int) theNestedListProperty["Count"]; index++)
{
var itemOfOuterList = theNestedListProperty[index.ToString()] as Dictionary<string, object>;
if ((bool) itemOfOuterList["IsCollection"])
{
// Retrieve the double values
for (var nestedListIndex = 0; nestedListIndex < (int) itemOfOuterList["Count"]; nestedListIndex++)
{
var innerListValue = (double) itemOfOuterList[nestedListIndex.ToString()];
}
}
}
}
}
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