将查询字符串数组参数转换为字典值

本文关键字:字典 转换 参数 查询 字符串 数组 | 更新日期: 2023-09-27 17:57:23

>我正在尝试接受表单的查询字符串

?param[key1]=value1&param[key2]=value2

或者

?where[column1]=value1&where[column2]=value1
&orderby[column1]=asc&orderby[column2]=desc

并将其转换为 C# MVC 4 Web API 中的字典,以便可以作为param["key1"]访问它。这在 PHP 中是微不足道的,但我无法找到任何在 C# 中重现它的方法。

我知道我可以通过

?param=value1&param=value2

这样我就可以做类似的事情

?where=column1&where=column2
&whereval=value1&whereval=value2
&orderby=column1&orderby=column2
&orderbycard=asc&orderbycard=desc

但这留下了订购问题,对我的目的几乎没有用处。

希望这不是框架的限制,我如何在 C# 中实现字典样式的转换,最好是这样我就可以在函数的参数中获取字典?

澄清一下:我不打算将查询字符串转换为 NVC,而是将查询字符串参数转换为自己的 NVC。这并不像 ParseQueryString() 或 Request.QueryString 对象那么简单。

这个问题

上次被关闭了,理由是这个问题是重复的,所以我补充了澄清。我会要求任何考虑将其标记为重复的人在这样做之前考虑我在这里问的内容,并认真查看我的示例。

将查询字符串数组参数转换为字典值

没有内置的方法可以实现该结果。下面是我的尝试,使用带有命名组的正则表达式。可以跳过拆分并处理正则表达式本身中的?&字符,但我采用这种方法来保持模式更干净。

string input = "?where[column]=value&where[column2]=value2&orderby[column]=asc&orderby[column2]=desc";
string pattern = @"(?<Type>[^[]+)'[(?<Key>[^]]+)']=(?<Value>.+)";
var re = new Regex(pattern);
var dict = input.Split(new[] { "?", "&" }, StringSplitOptions.RemoveEmptyEntries)
                .Select(s => re.Match(s))
                .GroupBy(m => m.Groups["Type"].Value)
                .ToDictionary(g => g.Key,
                    g => g.ToDictionary(x => x.Groups["Key"].Value,
                                        x => x.Groups["Value"].Value));
foreach (var t in dict.Keys)
{
    Console.WriteLine("Type: " + t);
    foreach (var k in dict[t].Keys)
    {
        Console.WriteLine("{0}: {1}", k, dict[t][k]);
    }
}

当然,这取决于您知道事先要检查的钥匙,例如dict["where"]["column"]

您可能会发现ContainsKeyTryGetValue方法有助于检查密钥是否存在:

string result;
if (dict["where"].TryGetValue("column", out result))
{
    Console.WriteLine(result);
}

你可以尝试这样的事情

var queryString = "http://example.com/api/objects?where[id]=4&orderby[id]=asc?";
string[] arr = queryString.Split(new char[] { '&' });
int cntVar = 0; // start key
Dictionary<int, string> dictQuery;
dictQuery = arr.ToDictionary(d => ++cntVar);
我想

和大家分享我根据艾哈迈德·法吉德的工作得出的东西。(顺便说一下,这是一个很好的开始。我需要相同的功能,但我需要它能够接受索引链中的更多深度。

这使您可以根据需要读取任意数量的子索引,而无需对其进行硬编码以停止在 2 或 3 级深度。因此,通过此更新,您可以像这样读取这样的查询字符串...

columns[0][data]=0&columns[0][name]=&columns[0][searchable]=true  
&columns[0][orderable]=true&columns[0][search][value]=  
&columns[0][search][regex]=false&columns[1][data]=1  
&columns[1][name]=&columns[1][searchable]=true  
&columns[1][orderable]=true&columns[1][search][value]=  
&columns[1][search][regex]=false&columns[2][data]=2  
...  
&order[0][column]=0&order[0][dir]=asc&start=0&length=10&search[value]=&search[regex]=false

这是我对该部分代码的更新版本。

public IDictionary ConvertQueryString(string sQry)
{
    string pattern = @"(?<Prop>[^[]+)(?:'[(?<Key>[^]]+)'])*=(?<Value>.*)";
    // The brackets seem to be encoded as %5B and %5D
    var qry = HttpUtility.UrlDecode(sQry);
    var re = new Regex(pattern);
    var dict = qry.Split(new[] { "?", "&" }, StringSplitOptions.RemoveEmptyEntries)
               .Select(s => re.Match(s)).Where(g => g.Success)
               .GroupBy(m => m.Groups["Prop"].Value)
               .ToDictionary<IGrouping<string, Match>, string, object>(
                    g => g.Key, 
                    g => GetKey(g, 0));
    return dict;
}
private object GetKey(IGrouping<string, Match> grouping, int level)
{
    var count = grouping.FirstOrDefault().Groups["Key"].Captures.Count;
    // If the level is equal to the captures, then we are at the end of the indexes
    if (count == level)
    {
        var gValue = grouping.Where(gr => gr.Success).FirstOrDefault();
        var value = gValue.Groups["Value"].Value;
        return value;
    }
    else
    {
        return grouping.Where(gr => gr.Success)
                .GroupBy(m => m.Groups["Key"].Captures[level].Value)
                .ToDictionary<IGrouping<string, Match>, string, object>(
                        a => a.Key, 
                        a => GetKey(a, level + 1));
    }
}

我想走得更远,而不仅仅是构建一个嵌套的字典对象,我还想基于传递的字符串实际构建对象。我在新版本的jQuery DataTables中需要这个,它实际上是产生上面那个令人讨厌的查询字符串的原因。 这是一个快速的写作,所以它有点粗糙,可能有点容易出错,因为我还没有时间加固它。

// Call it like so.
MyClass object = new MyClass();
WalkDictionary(dict, object);
// At this point the object will be filled out for you.
public class MyClass
{
    public List<cColumns> columns { get; set; }
    public cSearch search { get; set; }
}
public class cColumns
{
    public int? data { get; set; }
    public string name { get; set; }
    public bool? searchable { get; set; }
    public bool? orderable { get; set; }
    public cSearch search { get; set; }
}
public class cSearch
{
    public string value { get; set; }
    public bool? regex { get; set; }
}

我只放我需要的基本类型。因此,如果您需要/想要更多,则必须添加它们。

private void WalkDictionary(IDictionary dict, object obj)
{
    foreach (string key in dict.Keys)
    {
        Type t = obj.GetType();
        if (t.IsGenericType
            && typeof(List<>) == t.GetGenericTypeDefinition())
        {
            Type[] typeParameters = t.GetGenericArguments();
            foreach (DictionaryEntry item in dict)
            {
                var lstPropObj = Activator.CreateInstance(typeParameters[0]);
                WalkDictionary((IDictionary)item.Value, lstPropObj);
                ((IList)obj).Add(lstPropObj);
            }
            return;
        }
        PropertyInfo prop = obj.GetType().GetProperty(key);
        if (prop == null)
            continue;
        if (dict[key] is IDictionary)
        {
            //Walk
            var objProp = prop.GetValue(obj);
            if (objProp == null)
                objProp = Activator.CreateInstance(prop.PropertyType);
            WalkDictionary(dict[key] as IDictionary, objProp);
            prop.SetValue(obj, objProp);
        }
        else if (prop.PropertyType.IsAssignableFrom(typeof(int)))
        {
            int val = Convert.ToInt32(dict[key]);
            prop.SetValue(obj, val);
        }
        else if (prop.PropertyType.IsAssignableFrom(typeof(bool)))
        {
            bool val = Convert.ToBoolean(dict[key]);
            prop.SetValue(obj, val);
        }
        else if (prop.PropertyType.IsAssignableFrom(typeof(string)))
        {
            string val = Convert.ToString(dict[key]);
            prop.SetValue(obj, val);
        }
    }
}