c# - 将类实例转换为字典以进行迭代

标签 c# json.net

我想要一个通用函数来将任何类实例转换为字典,这样我就可以轻松地以通用方式使用它。在我的例子中,我想将任何实例 SSF.Mod 转换为一个 dict,我可以迭代它以基于它动态生成 UI 的一部分。

我考虑过使用 json.net,但快速检查后发现没有人要求完全相同的东西。

SSF.Mod类的缩减版本:

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;
            } }
     }
}

将示例实例缩减为 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": "",
  }

我想过类似的事情

Dictionary<string, object> dictOfMod = instanceOfMod.toDict();
foreach(var entry in dictOfMod){
   if (entry is bool) etc...

最佳答案

将实例转换为 Dictionary :

Mod mod = new Mod();

var modDictionary = mod.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
         .ToDictionary(
           propertyInfo => propertyInfo.Name,
           propertyInfo => propertyInfo.GetValue(mod));

编辑

以下方法将类型及其完整声明类型树转换为 Dictionary<string, object>

备注

默认KeyDictionary<string, object>是属性名。

集合被转换为 Dictionary<string, object> Key在哪里是项目的索引。
这种字典的最后一项是 Key 所在的计数。是"Count" .
确定是否 Dictionary<string, object>是一个使用 Key 的集合"IsCollection"它返回一个 bool 值。

您可以使用此扩展方法:

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()];
        }
      }
    }
  }
}

关于c# - 将类实例转换为字典以进行迭代,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57845289/

相关文章:

c# - 从 Json 字符串中提取数据

wcf - 如何将 Json.Net 设置为 WCF REST 服务的默认序列化程序

c# - 在不闪烁的情况下在双缓冲控件上重新绘制图像时出现问题

c# - 我可以使用 OleDB 获取 Excel 行号吗?

c# - 添加范围然后查找重复项或检查每个项目的冲突是否更快?

c# - 正则表达式可以在正向查找后进行多次匹配(自动换行后)吗?

json.net - 无法转换类型为 Newtonsoft.Json.Linq.JObject 的对象,即使我试图转换为具有匹配属性的对象

c# - 使用 JSON.NET 升级 JSON 文档

c# - 要序列化的同名属性

c# - "Items collection cannot be modified when the DataSource property is set."