将字符串转换为多级字典

3

我有一个文本文件,看起来像这样:

tabs.main="******"
tabs.settings="******"

settings.setting1="******"
settings.setting2="******"
settings.setting3="******"
settings.setting4="******"

settings.setting5.title="******"
settings.setting5.settingoption1="******"
settings.setting5.settingoption2="******"

我希望能将其解析成一个多级字典。例如,我会有一个根字典,它看起来像这样Dictionary<string, object>,在其中我会有选项卡和设置,它们本身也是Dictionary<string, object>。以图形形式呈现,我想要的是:
Dictionary<string, object>
   -> Dictionary<"tabs", object>
      -> Dictionary<"main", "*******">
      -> Dictionary<"settings", "*******">
   -> Dictionary<"settings", object>
      -> Dictionary<"setting1", "*******">

等等,诸如此类的内容。这是可能的吗?如果可以,是否有人能指点我正确的方向。

4个回答

1

词典并不是为了以那种方式存储数据而设计的。您可以创建类来表示文件并将数据加载到其中。根据您展示的内容,可能会像这样工作。

public class File
{
    private List<Setting> settings = new List<Setting>();
    public Tab Tab { get; set; }
    public List<Setting> Settings { get{ return settings; } }
}

public class Tab
{
    public string Main{ get; set;}
    public string Settings{ get; set;}
}

public class Setting
{
    private List<string> options = new List<string>();
    public string Value{ get; set; }
    public List<string> Options{ get{ return options;} }
}

这将用于DynamicObject反序列化器,因此预定义的类布局并不理想。但是,如果内容是静态的,这就是我会做的方式!谢谢! - jduncanator

1
你将要遇到的问题是,你的一些键需要与值(可能是字符串)一起存储在字典中,而另一些键则需要与其他字典一起存储。
在你的例子中,你列出了:
settings.setting5="******"
settings.setting5.settingoption1="******"

这两行代码互相矛盾。settings.settings5应该将键settings5的值存储在字典中,但下一行却期望settings5是其他值的字典。
您可以编写如下的代码块:
var settings = new Dictionary<string, object>();
var lines = File.ReadAllLines("...");
foreach (var line in lines)
{
    var parts = line.Split(new char[] { '=' }, 2);
    if (parts.Length != 2) continue;

    var keys = parts[0].Split('.');
    var value = parts[1];

    var dict = settings;
    for (int i = 0; i < keys.Length - 1; i++)
    {
        if (!dict.ContainsKey(keys[i]))
            dict.Add(keys[i], new Dictionary<string, object>());
        dict = (Dictionary<string, object>)dict[keys[i]];
    }

    dict.Add(keys[keys.Length - 1], value);
}
  1. 它没有任何错误检查。
  2. 你会发现,虽然它可以填充你的对象,但从中检索值是很麻烦的。因为你必须将每个结果转换为字符串或另一个字典。

无论如何,你会发现你最好避免尝试为此目的创建字典的字典。


我知道,我在发布问题后意识到了这一点。我已经相应地进行了修改。这实际上是我最终采取的方法!而且疼痛实际上是在我将要用来访问字典的DynamicObject中处理的! - jduncanator

0
请考虑使用字典以外的其他对象,来回答您的问题。
请注意,这种数据结构不允许同时存在相同键名的字典和字符串值。
static Dictionary<string,object> Parse(string contents)
{
   var root = new Dictionary<string,object>();

   using (var rdr = new StringReader(contents))
   {
      string line;
      var equals = new [] {'='};
      while(null != (line = rdr.ReadLine()))
      {
         if(!string.IsNullOrEmpty(line))
         {
            var keyValue = line.Split(equals, 2);
            AddValue(root, keyValue[0], keyValue[1]);
         }
      }
   }

   return root;
}

static void AddValue(Dictionary<string,object> dict, string dottedKeys, string value)
{
   string [] keys = dottedKeys.Split('.');
   for(var i = 0; i < keys.Length - 1; i++)
   {
      var key = keys[i];
      dict = GetOrAdd(dict, key);
   }
   dict[keys[keys.Length - 1]] = value;
}

static Dictionary<string,object> GetOrAdd(Dictionary<string,object> parent, string key)
{
   object o;
   Dictionary<string,object> childDict;
   if(parent.TryGetValue(key, out o))
      childDict = (Dictionary<string,object>) o;  // This will throw when adding a dictionary to a value.
   else
      parent[key] = childDict = new Dictionary<string,object>();
   return childDict;
}

static string fileContents=@"
tabs.main=""******""
tabs.settings=""******""

settings.setting1=""******""
settings.setting2=""******""
settings.setting3=""******""
settings.setting4=""******""
settings.setting5=""******""

settings.setting6.settingoption1=""******""
settings.setting6.settingoption2=""******""
";

0

实际上,我最终设法在这段时间里自己编写了一些代码,但是由于我不想浪费回答这个问题的人们的时间,所以我将把Jason的答案标记为答案,因为这与我想出的有点类似,而他也帮助我编写了我的代码!

Dictionary<string, object> _dic = new Dictionary<string, object>();
using(StreamReader reader = new StreamReader(file))
{
    string text;
    while ((text = reader.ReadLine()) != null)
    {
        if (text.Equals(String.Empty))
            continue;
        string line = text.Trim();
        string[] keyval = line.Split('=');
        string value = keyval[1].Trim('"');
        string[] keys = keyval[0].Split('.');
        Dictionary<string, object> prevDic = _dic;
        for (int i = 0; i < keys.Length; i++)
        {
            if (i + 1 != keys.Length)
            {
                Dictionary<string, object> temp = new Dictionary<string, object>();
                if (prevDic.ContainsKey(keys[i]))
                {
                    prevDic = prevDic[keys[i]] as Dictionary<string, object>; // Avoids exceptions when trying to cast the string value to a dictionary
                    continue;
                }
                else
                {
                    prevDic.Add(keys[i], temp);
                    prevDic = temp;
                }
            }
            else
            {
                prevDic.Add(keys[i], value);
            }
        }
    }
}

感谢大家对此的帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接