I have this code:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (KeyValuePair<String,String> pr in nv) {
//process KeyValuePair
}
This compiles, but when I try to run it I get an InvalidCastException.
Why is this? Why can't I use KeyValuePair to iterate over a NameValueCollection, and what should I use instead?
First of all, NameValueCollection doesn't use KeyValuePair<String,String>. Also, foreach only exposes the key:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (string key in nv) {
var value = nv[key];
}
You can't do that directly, but you can create an extension method like so:
public static IEnumerable<KeyValuePair<string, string>> AsKVP(
this NameValueCollection source
)
{
return source.AllKeys.SelectMany(
source.GetValues,
(k, v) => new KeyValuePair<string, string>(k, v));
}
Then you can do:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (KeyValuePair<String,String> pr in nv.AsKVP()) {
//process KeyValuePair
}
Note: inspired by this. SelectMany is required to handle duplicate keys.
vb.net version:
<Extension>
Public Function AsKVP(
source As Specialized.NameValueCollection
) As IEnumerable(Of KeyValuePair(Of String, String))
Dim result = source.AllKeys.SelectMany(
AddressOf source.GetValues,
Function(k, v) New KeyValuePair(Of String, String)(k, v))
Return result
End Function
For future reference, you could also use this syntax:
foreach(string key in Request.QueryString)
{
var value = Request.QueryString[key];
}
ANother extension method, for learning purposes:
public static IEnumerable<KeyValuePair<string, string>> ToIEnumerable(this NameValueCollection nvc)
{
foreach (string key in nvc.AllKeys)
{
yield return new KeyValuePair<string, string>(key, nvc[key]);
}
}
NameValueCollection uses the old-skool enumerator:
var enu = ConfigurationManager.AppSettings.GetEnumerator();
while(enu.MoveNext())
{
string key = (string)enu.Current;
string value = ConfigurationManager.AppSettings[key];
}
I did like this and it works:
foreach (string akey in request.Query.Keys.Cast<string>())
writer.WriteLine(akey + " = " + request.Query[akey]);
Be aware that the key name might appear more than once in the query string and that the comparison is usually case sensitive.
If you want to just get the value of the first matching key and not bothered about case then use this:
public string GetQueryValue(string queryKey)
{
foreach (string key in QueryItems)
{
if(queryKey.Equals(key, StringComparison.OrdinalIgnoreCase))
return QueryItems.GetValues(key).First(); // There might be multiple keys of the same name, but just return the first match
}
return null;
}
To convert nameValueCollection from QueryString to List you can use extension like this:
public static List<KeyValuePair<string, string>> GetParams(this HttpRequest request)
{
var nameValueCollection = HttpUtility.ParseQueryString(request.QueryString.Value);
List<KeyValuePair<string, string>> keyValueCollections = new List<KeyValuePair<string, string>>();
if (!nameValueCollection.IsNullOrEmpty())
{
foreach (var key in nameValueCollection.AllKeys)
{
keyValueCollections.Add(new KeyValuePair<string, string>(key, nameValueCollection[key]));
}
}
return keyValueCollections;
}
public static void PrintKeysAndValues2( NameValueCollection myCol )
{
Console.WriteLine( " [INDEX] KEY VALUE" );
for ( int i = 0; i < myCol.Count; i++ )
Console.WriteLine( " [{0}] {1,-10} {2}", i, myCol.GetKey(i), myCol.Get(i) );
Console.WriteLine();
}
http://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection.aspx
Related
Let's say I have the following class:
public class TestClass
{
public Dictionary<string, string> Property1 { get; set; }
}
If I do the following:
var dictionary = new Dictionary<string, string>();
dictionary.add("key1", "value1");
dictionary.add("key2", "value2");
var newClass = new TestClass();
newClass.Property1 = dictionary;
I am trying to URL encode this dictionary to the following:
https://baseaddress.com/resource/?Property1[key1]=value1&Property1[key2]=value2
When attempting to URL encode the dictionary via HttpUtility it is returning the ToString() method of the Dictionary which comes out as:
System.Collections.Generic.Dictionary`2[System.String,System.String]
I am trying to pass this dictionary to a .netcore API that binds to a similar Dictionary<string, string>
Edit
I was able to get it working by using the following code:
var builder = new UriBuilder(uri);
var query = HttpUtility.ParseQueryString(string.Empty);
foreach (var propInfo in obj.GetType().GetProperties())
{
var propName = propInfo.Name;
var propValue = propInfo.GetValue(obj);
if (propValue != null)
{
var dict = propValue as IDictionary;
if (dict != null)
{
foreach (var key in dict.Keys)
{
var keyName = key;
var keyValue = dict[key];
query.Add($"{propName}[{keyName}]", keyValue.ToString());
}
}
else
{
query.Add(propName, propValue.ToString());
}
}
}
builder.Query = query.ToString();
return builder.Uri;
I was hoping there was a more efficient way to make this work.
If you want to get the standard format and avoid any problem with your QueryString you can "leverage" .net core approach which indeed is a way larger than the old approach. With that said here is what you can do:
One thing....Notice that they are strings so you can add your brackets :)
Dictionary<String,StringValues>() queryString = QueryHelpers.ParseQuery("?param1=value");
StringValues secondValue=StringValues.Concat(queryString["param2"], "my other value");
parsedQueryString["yourkey"] = secondValue;
//At this point you can start concatenating as many time as needed.
QueryString.Create(parsedQueryString).ToString();
// creates the following string "?param1=value¶m2=my%20other%20value"
A plus :)
// Getting a param value
var param2Value = queryString["param2"];
param2Value.ToString(); // Get the values concatenated together
param2Value.ToArray(); // Gets an array of strings
// Modifying a parameter
queryString["param1"] = "another value";
// NOTE, if there were two values, this overwrites both and leaves a single value
I'm working with a dictionary of type <string,string> like this:
Dictionary<string, string> parameters = new Dictionary<string, string>()
{
{"llave1", "valor1"},
{"llave2", "valor2"},
{"llave3", "valor3"},
{"llave4", "valor4"}
};
I want to get a string like this:
"llave1=valor1&llave2=valor2&llave3=valor3&llave4=valor4"
to solve this problem I made this:
foreach (var element in parameters)
{
strParameters += element.Key + "=" + element.Value;
if (index < parameters.Count)
{
strParameters += "&";
index++;
}
}
I wanted to know any way to get the same string result but using linq or String.Join I'm trying to refactory my code
I need to write search based on following criteria:
I need to find all records that match values of
key1 OR key2 OR key 3 values...etc
The number of keys and values is variable
List<KeyValuePair<string, string[]>> filterlist = new List<KeyValuePair<string, string[]>>()
{
new KeyValuePair<string, string[]>("Key1", new []{"jay","bloggs"}),
new KeyValuePair<string, string[]>("Key2", new []{"joe","blog","doe"}),
new KeyValuePair<string, string[]>("Key3", new []{"jon","blog"}),
};
Now my implementation
My current implementation does search but all expressions are "AND" instead of OR. I am not sure how to write it.
public class UserSearcher
{
private List<UserProfile> userProfiles;
public UserSearcher()
{
userProfiles = new List<UserProfile>();
}
public static List<UserProfile> SearchProfiles(List<KeyValuePair<string, string[]>> filterList)
{
var list = new List<UserProfile>();
var query = list.AsQueryable();
// search for each pair inside as or
foreach (KeyValuePair<string, string[]> searchPair in filterList)
{
foreach (string searchString in searchPair.Value)
{
string s = searchString;
// search for each item inside as and (has to contains all search strings
query = query.Where(x => x.PersonName.Contains(s));
}
}
return list = query.ToList();
}
}
The full example except db is:
https://gist.github.com/cpoDesign/acf69bc242ed0755597d
Use Predicate Builder - it works well.
So, if I got it right, you want to get back list of UserProfile where PersonName is inside any string[] of KeyValuePair list.
If so, try with this:
public static List<UserProfile> SearchProfiles(List<KeyValuePair<string, string[]>> filterList)
{
var list = new List<UserProfile>();
return list.Where(profile => filterList.Any(kvp => kvp.Value.Contains(profile.PersonName))).ToList();
}
Test example:
public static Expression<Func<T,bool>>
Or<T>(IEnumerable<Expression<Func<T,bool>>> expList){
ParameterExpression pe = Expression.Parameter(typeof(T));
Expression r = null;
foreach(var exp in expList){
r = r == null ? exp : Expression.Or(r,exp);
}
return Expression.Lambda<Func<T,bool>>(r.Body,pe);
}
var orList = new List<Expression<Func<T,bool>>>();
foreach (KeyValuePair<string, string[]> searchPair in filterList)
{
foreach (string searchString in searchPair.Value)
{
string s = searchString;
// search for each item inside as and
// (has to contains all search strings
orList.Add(x => x.PersonName.Contains(s));
}
}
query = query.Where( Or(expList));
I have to sort a namevaluecollection(Items usually 5 to 15) Against an enum(having items more than 60). Right now I am using this extension function, Anyone have better idea to write this code....
public static NameValueCollection Sort(this NameValueCollection queryString, Type orderByEnumType, bool excludeZeroValues)
{
NameValueCollection _processedQueryString = HttpUtility.ParseQueryString("");
if (queryString.HasKeys())
{
SortedList<int, KeyValuePair<string, string>> querySortedList = new SortedList<int, KeyValuePair<string, string>>();
string[] enumKeys = Enum.GetNames(orderByEnumType);
int counter = 1000;
foreach (string key in queryString)
{
string value = queryString[key];
if (enumKeys.Contains(key, StringComparer.CurrentCultureIgnoreCase))
{
int order = (int)Enum.Parse(orderByEnumType, key, true);
querySortedList.Add(order, new KeyValuePair<string, string>(key, value));
}
else
{
querySortedList.Add(counter, new KeyValuePair<string, string>(key, value));
counter++;
}
}
foreach (KeyValuePair<int, KeyValuePair<string, string>> kvp in querySortedList)
{
if (!kvp.Value.Value.IsNullOrEmpty() && !kvp.Value.Key.IsNullOrEmpty())
{
if (!excludeZeroValues || kvp.Value.Value != "0")
{
_processedQueryString.Add(kvp.Value.Key, System.Web.HttpUtility.UrlEncode(kvp.Value.Value));
}
}
}
}
return _processedQueryString;
}
This works like this
public enum OrderEnum
{
key1=1,
key2=20,
key3=3,
//note
key4=100,
key5=2,
key6=6,
key7,
key8,
key9
}
public void Test()
{
NameValueCollection col1 = new NameValueCollection();
col1.Add("key1", "value1");
col1.Add("key9", "value1");
col1.Add("key3", "value1");
col1.Add("key5", "value1");
Response.Write(col1.Sort(typeof(OrderEnum)).ToString());
//out put: key1=value1&key5=value1&key3=value1&key9=value1
}
This is should also work
public void Test2()
{
NameValueCollection col1 = new NameValueCollection();
col1.Add("key1", "value1");
col1.Add("key-x", "value1");
col1.Add("key-y", "value1");
col1.Add("key9", "value1");
col1.Add("key3", "value1");
col1.Add("key5", "value1");
col1.Add("key-z", "value1");
Response.Write(col1.Sort(typeof(OrderEnum)).ToString());
//out put: key1=value1&key5=value1&key3=value1&key9=value1&key-x=value1&key-y=value1&key-z=value1
}
I think its better to convert your namevaluecollection into List of keyvaluepairs and apply a simple LINQ order by operation, thats quick and simple.
Add a new extension method to convert your namevaluecoll into list of keyvaluepairs
public static List<KeyValuePair<string, string>> ToPairs(this System.Collections.Specialized.NameValueCollection collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
return collection.Cast<string>().Select(key => new KeyValuePair<string, string>(key, collection[key])).ToList();
}
And just apply the linq order by over this object, something like this
System.Collections.Specialized.NameValueCollection col1= new System.Collections.Specialized.NameValueCollection();
col1.Add("key1", "value1");
col1.Add("key-x", "value2");
col1.Add("key-y", "value3");
col1.Add("key9", "value4");
col1.Add("key3", "value5");
col1.Add("key5", "value6");
col1.Add("key-z", "value7");
var nvc = col1.ToPairs();
// To order the items based on key in descending order
var orderedbykey=nvc.OrderByDescending(x => x.Key).ToList();
// To order the items based on value in descending order
var orderedbyval=nvc.OrderByDescending(x => x.Value).ToList();
//or order by ur custom enum key
var orderbyEnumKeys = colc.OrderBy(x =>
{
int en;
try
{
en = (int)Enum.Parse(typeof(OrderEnum), x.Key);
}
catch (Exception ex)
{
return int.MaxValue;
}
return en;
}).ToList();
Hope this helps..
I finally Came to conclusion with this solution
public static NameValueCollection SortByEnum<TEnum>(this NameValueCollection source) where TEnum : struct
{
var orderedKeys = source.Keys.Cast<string>().OrderBy(k => ((Enum.IsDefined(typeof(TEnum), k)) ? ((int)Enum.Parse(typeof(TEnum), k)) : int.MaxValue));
var ordered = HttpUtility.ParseQueryString("");
foreach (var key in orderedKeys)
{
ordered.Add(key, source[key]);
}
return ordered;
}
This will resolve all my issues
Thanks #thomas #ramesh
You could do something like that :
public static NameValueCollection SortByEnum<TEnum>(this NameValueCollection source) where TEnum : struct
{
var orderedKeys = source.Keys.Cast<string>().OrderBy(k => Enum.IsDefined(typeof(TEnum), k) ? Convert.ToInt32(Enum.Parse(typeof(TEnum), k)) : int.MaxValue);
var ordered = new NameValueCollection();
foreach(var key in orderedKeys) ordered.Add(key, source[key]);
return ordered;
}
public static string DictToQueryString(Dictionary<string, string> data)
{
string querystring = "";
foreach (string key, string val in data)
querystring += key + "=" + val + "&";
return querystring;
}
How i foreach?
Your "code" would have an extraneous "&" on the end. Do you want this? It's likely that you don't want this but please correct if you do. Assuming not, the simplest approach is to let String.Join do its job:
public static string DictToQueryString(Dictionary<string, string> data) {
return String.Join(
"&",
data.Select(kvp => String.Format("{0}={1}", kvp.Key, kvp.Value))
.ToArray()
);
In C# 4.0 the call to ToArray will be obviated.
Like this:
public static string DictToQueryString(Dictionary<string, string> data)
{
StringBuilder queryString = new StringBuilder();
foreach(var pair in data)
{
if (queryString.Length > 0)
queryString.AppendFormat("&{0}={1}", pair.Key, pair.Value);
else
queryString.AppendFormat("{0}={1}", pair.Key, pair.Value);
}
return queryString.ToString();
}
I believe this is what you're looking for?
public static string DictToQueryString(Dictionary<string, string> data)
{
string querystring = "";
foreach (string key in data.Keys)
{
string val = data[key];
querystring += key + "=" + val + "&";
}
return querystring;
}