C#:递归获取具有深层字符串表达式的字典值

C#: Recursively get Dictonary value with deep string expression

我想使用字符串表达式作为键从 Dictionary<string, object> 中获取一个对象。
例如采用以下数据结构:

var data = new Dictionary<string, object>()
{
  { "Products", new object[]
    {
      new Dictionary<string, object>()
      {
        { "Name", "Tin of biscuits"  }
      }
    }
  }
}

我想 return 产品名称的表达式为:"Products[0].Name".
这只是一个例子,数据可以是任何深度,字符串表达式可以是 "BookStore.Books[3].Author.ContactDetails.Address" for eg.

到目前为止,我一直在尝试递归方法,也尝试过 string.Split('.')Aggregate Linq 方法,但我很挣扎,我认为这是一个完美的 SO 小谜题。

假设您的数据结构中始终包含 Dictionary<string, object>object[],则此方法应该有效:

private static object? GetSelectedValueOrDefault(this Dictionary<string, object> data, string selector)
{
    string[] selectorSegments = selector.Split('.');

    if (selectorSegments.Length == 0)
    {
        return null;
    }

    object? currentNode = data.GetValueOrDefault(selectorSegments[0]);

    if (currentNode == null)
    {
        return null;
    }

    for (int index = 1; index < selectorSegments.Length; index++)
    {
        string segment = selectorSegments[index];

        if (currentNode is not Dictionary<string, object> dictionary)
        {
            return null;
        }

        var selectorWithIndex = GetSelectorAndElementIndexOrDefault(segment);

        if (selectorWithIndex is not null &&
            currentNode is Dictionary<string, object> dict)
        {
            currentNode = dict.GetValueOrDefault(selectorWithIndex.Value.ItemSelector);
            currentNode = GetElementOrDefault(currentNode, selectorWithIndex.Value.Index);
            continue;
        }

        currentNode = dictionary.GetValueOrDefault(segment);

        if (index == selectorSegments.Length - 1)
        {
            return currentNode;
        }
    }

    return null;
}

private static object? GetElementOrDefault(object? currentNode, int index)
{
    if (currentNode is not object[] array)
    {
        return null;
    }

    if (index >= array.Length)
    {
        return null;
    }

    return array[index];
}

private static (string ItemSelector, int Index)? GetSelectorAndElementIndexOrDefault(string segment)
{
    if (!segment.Contains('['))
    {
        return null;
    }

    string[] result = segment.Split('[', ']');
    return (result[0], int.Parse(result[1]));
}

例子

var data = new Dictionary<string, object>()
{
    {
        "BookStore",
        new Dictionary<string, object>()
        {
            {
                "Name",
                "The Book Store"
            },
            {
                "Books",
                new object[]
                {
                    new Dictionary<string, object>(),
                    new Dictionary<string, object>(),
                    new Dictionary<string, object>(),
                    new Dictionary<string, object>()
                    {
                        {
                            "Author",
                            new Dictionary<string, object>()
                            {
                                {
                                    "Name",
                                    "Luke T O'Brien"
                                },
                                {
                                    "ContactDetails",
                                    new Dictionary<string, object>()
                                    {
                                        {
                                            "Address",
                                            "Some address"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
};

Console.WriteLine(data.GetSelectedValueOrDefault("BookStore.Name"));
Console.WriteLine(data.GetSelectedValueOrDefault("BookStore.Books[3].Author.Name"));
Console.WriteLine(data.GetSelectedValueOrDefault("BookStore.Books[3].Author.ContactDetails.Address"));

输出

The Book Store
Luke T O'Brien
Some address