3

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

?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 对象那么简单

这个问题上次因为是这个问题的重复而被关闭,所以我添加了澄清。我会要求任何考虑将此标记为重复的人在这样做之前考虑我在这里提出的问题,并认真查看我的示例。

4

3 回答 3

4

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

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);
}
于 2013-02-02T02:59:57.070 回答
0

你可以试试这样的

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);
于 2013-02-01T18:38:42.010 回答
0

我想与您分享我根据 Ahmad Mageed 的工作得出的结论。(顺便说一句,这是一个很好的开始。)我需要相同的功能,但我需要它能够接受索引链中的更多深度。

这使您能够根据需要读取尽可能多的子索引,而无需对其进行硬编码以停止在 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);
        }
    }
}
于 2017-02-24T23:53:33.980 回答