How to bulid a url string with multiple parameters [duplicate] - c#
A common task when calling web resources from a code is building a query string to including all the necessary parameters. While by all means no rocket science, there are some nifty details you need to take care of like, appending an & if not the first parameter, encoding the parameters etc.
The code to do it is very simple, but a bit tedious:
StringBuilder SB = new StringBuilder();
if (NeedsToAddParameter A)
{
SB.Append("A="); SB.Append(HttpUtility.UrlEncode("TheValueOfA"));
}
if (NeedsToAddParameter B)
{
if (SB.Length>0) SB.Append("&");
SB.Append("B="); SB.Append(HttpUtility.UrlEncode("TheValueOfB")); }
}
This is such a common task one would expect a utility class to exist that makes it more elegant and readable. Scanning MSDN, I failed to find one—which brings me to the following question:
What is the most elegant clean way you know of doing the above?
You can create a new writeable instance of HttpValueCollection by calling System.Web.HttpUtility.ParseQueryString(string.Empty), and then use it as any NameValueCollection. Once you have added the values you want, you can call ToString on the collection to get a query string, as follows:
NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);
queryString.Add("key1", "value1");
queryString.Add("key2", "value2");
return queryString.ToString(); // Returns "key1=value1&key2=value2", all URL-encoded
The HttpValueCollection is internal and so you cannot directly construct an instance. However, once you obtain an instance you can use it like any other NameValueCollection. Since the actual object you are working with is an HttpValueCollection, calling ToString method will call the overridden method on HttpValueCollection, which formats the collection as a URL-encoded query string.
After searching SO and the web for an answer to a similar issue, this is the most simple solution I could find.
.NET Core
If you're working in .NET Core, you can use the Microsoft.AspNetCore.WebUtilities.QueryHelpers class, which simplifies this greatly.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers
Sample Code:
const string url = "https://customer-information.azure-api.net/customers/search/taxnbr";
var param = new Dictionary<string, string>() { { "CIKey", "123456789" } };
var newUrl = new Uri(QueryHelpers.AddQueryString(url, param));
If you look under the hood the QueryString property is a NameValueCollection. When I've done similar things I've usually been interested in serialising AND deserialising so my suggestion is to build a NameValueCollection up and then pass to:
using System.Linq;
using System.Web;
using System.Collections.Specialized;
private string ToQueryString(NameValueCollection nvc)
{
var array = (
from key in nvc.AllKeys
from value in nvc.GetValues(key)
select string.Format(
"{0}={1}",
HttpUtility.UrlEncode(key),
HttpUtility.UrlEncode(value))
).ToArray();
return "?" + string.Join("&", array);
}
I imagine there's a super elegant way to do this in LINQ too...
With the inspiration from Roy Tinker's comment, I ended up using a simple extension method on the Uri class that keeps my code concise and clean:
using System.Web;
public static class HttpExtensions
{
public static Uri AddQuery(this Uri uri, string name, string value)
{
var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);
httpValueCollection.Remove(name);
httpValueCollection.Add(name, value);
var ub = new UriBuilder(uri);
ub.Query = httpValueCollection.ToString();
return ub.Uri;
}
}
Usage:
Uri url = new Uri("http://localhost/rest/something/browse").
AddQuery("page", "0").
AddQuery("pageSize", "200");
Edit - Standards compliant variant
As several people pointed out, httpValueCollection.ToString() encodes Unicode characters in a non-standards-compliant way. This is a variant of the same extension method that handles such characters by invoking HttpUtility.UrlEncode method instead of the deprecated HttpUtility.UrlEncodeUnicode method.
using System.Web;
public static Uri AddQuery(this Uri uri, string name, string value)
{
var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);
httpValueCollection.Remove(name);
httpValueCollection.Add(name, value);
var ub = new UriBuilder(uri);
// this code block is taken from httpValueCollection.ToString() method
// and modified so it encodes strings with HttpUtility.UrlEncode
if (httpValueCollection.Count == 0)
ub.Query = String.Empty;
else
{
var sb = new StringBuilder();
for (int i = 0; i < httpValueCollection.Count; i++)
{
string text = httpValueCollection.GetKey(i);
{
text = HttpUtility.UrlEncode(text);
string val = (text != null) ? (text + "=") : string.Empty;
string[] vals = httpValueCollection.GetValues(i);
if (sb.Length > 0)
sb.Append('&');
if (vals == null || vals.Length == 0)
sb.Append(val);
else
{
if (vals.Length == 1)
{
sb.Append(val);
sb.Append(HttpUtility.UrlEncode(vals[0]));
}
else
{
for (int j = 0; j < vals.Length; j++)
{
if (j > 0)
sb.Append('&');
sb.Append(val);
sb.Append(HttpUtility.UrlEncode(vals[j]));
}
}
}
}
}
ub.Query = sb.ToString();
}
return ub.Uri;
}
Flurl [disclosure: I'm the author] supports building query strings via anonymous objects (among other ways):
var url = "http://www.some-api.com".SetQueryParams(new
{
api_key = ConfigurationManager.AppSettings["SomeApiKey"],
max_results = 20,
q = "Don't worry, I'll get encoded!"
});
The optional Flurl.Http companion lib allows you to do HTTP calls right off the same fluent call chain, extending it into a full-blown REST client:
T result = await "https://api.mysite.com"
.AppendPathSegment("person")
.SetQueryParams(new { ap_key = "my-key" })
.WithOAuthBearerToken("MyToken")
.PostJsonAsync(new { first_name = firstName, last_name = lastName })
.ReceiveJson<T>();
The full package is available on NuGet:
PM> Install-Package Flurl.Http
or just the stand-alone URL builder:
PM> Install-Package Flurl
Curious that no one has mentioned QueryBuilder from AspNet.Core.
It's helpful when you have a query with duplicate key like ?filter=a&filter=b
var qb = new QueryBuilder();
qb.Add("filter", new string[] {"A", "B"});
Then you'll just add qb to the URI, it is converted automatically to string.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.extensions.querybuilder?view=aspnetcore-5.0
I answered a similar question a while ago. Basically, the best way would be to use the class HttpValueCollection, which ASP.NET's Request.QueryString property actually is, unfortunately it is internal in the .NET framework.
You could use Reflector to grab it (and place it into your Utils class). This way you could manipulate the query string like a NameValueCollection, but with all the url encoding/decoding issues taken care for you.
HttpValueCollection extends NameValueCollection, and has a constructor that takes an encoded query string (ampersands and question marks included), and it overrides a ToString() method to later rebuild the query string from the underlying collection.
Example:
var coll = new HttpValueCollection();
coll["userId"] = "50";
coll["paramA"] = "A";
coll["paramB"] = "B";
string query = coll.ToString(true); // true means use urlencode
Console.WriteLine(query); // prints: userId=50¶mA=A¶mB=B
Here's a fluent/lambda-ish way as an extension method (combining concepts in previous posts) that supports multiple values for the same key. My personal preference is extensions over wrappers for discover-ability by other team members for stuff like this. Note that there's controversy around encoding methods, plenty of posts about it on Stack Overflow (one such post) and MSDN bloggers (like this one).
public static string ToQueryString(this NameValueCollection source)
{
return String.Join("&", source.AllKeys
.SelectMany(key => source.GetValues(key)
.Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))))
.ToArray());
}
edit: with null support, though you'll probably need to adapt it for your particular situation
public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries)
{
return source != null ? String.Join("&", source.AllKeys
.Where(key => !removeEmptyEntries || source.GetValues(key)
.Where(value => !String.IsNullOrEmpty(value))
.Any())
.SelectMany(key => source.GetValues(key)
.Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value))
.Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty)))
.ToArray())
: string.Empty;
}
Here's my late entry. I didn't like any of the others for various reasons, so I wrote my own.
This version features:
Use of StringBuilder only. No ToArray() calls or other extension methods. It doesn't look as pretty as some of the other responses, but I consider this a core function so efficiency is more important than having "fluent", "one-liner" code which hide inefficiencies.
Handles multiple values per key. (Didn't need it myself but just to silence Mauricio ;)
public string ToQueryString(NameValueCollection nvc)
{
StringBuilder sb = new StringBuilder("?");
bool first = true;
foreach (string key in nvc.AllKeys)
{
foreach (string value in nvc.GetValues(key))
{
if (!first)
{
sb.Append("&");
}
sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
first = false;
}
}
return sb.ToString();
}
Example Usage
var queryParams = new NameValueCollection()
{
{ "x", "1" },
{ "y", "2" },
{ "foo", "bar" },
{ "foo", "baz" },
{ "special chars", "? = &" },
};
string url = "http://example.com/stuff" + ToQueryString(queryParams);
Console.WriteLine(url);
Output
http://example.com/stuff?x=1&y=2&foo=bar&foo=baz&special%20chars=%3F%20%3D%20%26
I needed to solve the same problem for a portable class library (PCL) that I'm working on. In this case, I don't have access to System.Web so I can't use ParseQueryString.
Instead I used System.Net.Http.FormUrlEncodedContent like so:
var url = new UriBuilder("http://example.com");
url.Query = new FormUrlEncodedContent(new Dictionary<string,string>()
{
{"param1", "val1"},
{"param2", "val2"},
{"param3", "val3"},
}).ReadAsStringAsync().Result;
How about creating extension methods that allow you to add the parameters in a fluent style like this?
string a = "http://www.somedomain.com/somepage.html"
.AddQueryParam("A", "TheValueOfA")
.AddQueryParam("B", "TheValueOfB")
.AddQueryParam("Z", "TheValueOfZ");
string b = new StringBuilder("http://www.somedomain.com/anotherpage.html")
.AddQueryParam("A", "TheValueOfA")
.AddQueryParam("B", "TheValueOfB")
.AddQueryParam("Z", "TheValueOfZ")
.ToString();
Here's the overload that uses a string:
public static string AddQueryParam(
this string source, string key, string value)
{
string delim;
if ((source == null) || !source.Contains("?"))
{
delim = "?";
}
else if (source.EndsWith("?") || source.EndsWith("&"))
{
delim = string.Empty;
}
else
{
delim = "&";
}
return source + delim + HttpUtility.UrlEncode(key)
+ "=" + HttpUtility.UrlEncode(value);
}
And here's the overload that uses a StringBuilder:
public static StringBuilder AddQueryParam(
this StringBuilder source, string key, string value)
{
bool hasQuery = false;
for (int i = 0; i < source.Length; i++)
{
if (source[i] == '?')
{
hasQuery = true;
break;
}
}
string delim;
if (!hasQuery)
{
delim = "?";
}
else if ((source[source.Length - 1] == '?')
|| (source[source.Length - 1] == '&'))
{
delim = string.Empty;
}
else
{
delim = "&";
}
return source.Append(delim).Append(HttpUtility.UrlEncode(key))
.Append("=").Append(HttpUtility.UrlEncode(value));
}
public static string ToQueryString(this Dictionary<string, string> source)
{
return String.Join("&", source.Select(kvp => String.Format("{0}={1}", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))).ToArray());
}
public static string ToQueryString(this NameValueCollection source)
{
return String.Join("&", source.Cast<string>().Select(key => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(source[key]))).ToArray());
}
Add this class to your project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
public class QueryStringBuilder
{
private readonly List<KeyValuePair<string, object>> _list;
public QueryStringBuilder()
{
_list = new List<KeyValuePair<string, object>>();
}
public void Add(string name, object value)
{
_list.Add(new KeyValuePair<string, object>(name, value));
}
public override string ToString()
{
return String.Join("&", _list.Select(kvp => String.Concat(Uri.EscapeDataString(kvp.Key), "=", Uri.EscapeDataString(kvp.Value.ToString()))));
}
}
And use it like this:
var actual = new QueryStringBuilder {
{"foo", 123},
{"bar", "val31"},
{"bar", "val32"}
};
actual.Add("a+b", "c+d");
actual.ToString(); // "foo=123&bar=val31&bar=val32&a%2bb=c%2bd"
Untested, but I think something along these lines would work quite nicely
public class QueryString
{
private Dictionary<string,string> _Params = new Dictionary<string,string>();
public overide ToString()
{
List<string> returnParams = new List<string>();
foreach (KeyValuePair param in _Params)
{
returnParams.Add(String.Format("{0}={1}", param.Key, param.Value));
}
// return String.Format("?{0}", String.Join("&", returnParams.ToArray()));
// credit annakata
return "?" + String.Join("&", returnParams.ToArray());
}
public void Add(string key, string value)
{
_Params.Add(key, HttpUtility.UrlEncode(value));
}
}
QueryString query = new QueryString();
query.Add("param1", "value1");
query.Add("param2", "value2");
return query.ToString();
My offering:
public static Uri AddQuery(this Uri uri, string name, string value)
{
// this actually returns HttpValueCollection : NameValueCollection
// which uses unicode compliant encoding on ToString()
var query = HttpUtility.ParseQueryString(uri.Query);
query.Add(name, value);
var uriBuilder = new UriBuilder(uri)
{
Query = query.ToString()
};
return uriBuilder.Uri;
}
Usage:
var uri = new Uri("http://stackoverflow.com").AddQuery("such", "method")
.AddQuery("wow", "soFluent");
// http://stackoverflow.com?such=method&wow=soFluent
In dotnet core QueryHelpers.AddQueryString() will accept an IDictionary<string,string> of key-value pairs. To save a few memory allocs and CPU cycles you can use SortedList<,> instead of Dictionary<,>, with an appropriate capacity and items added in sort order...
var queryParams = new SortedList<string,string>(2);
queryParams.Add("abc", "val1");
queryParams.Add("def", "val2");
string requestUri = QueryHelpers.AddQueryString("https://localhost/api", queryParams);
Combined the top answers to create an anonymous object version:
var queryString = HttpUtility2.BuildQueryString(new
{
key2 = "value2",
key1 = "value1",
});
That generates this:
key2=value2&key1=value1
Here's the code:
public static class HttpUtility2
{
public static string BuildQueryString<T>(T obj)
{
var queryString = HttpUtility.ParseQueryString(string.Empty);
foreach (var property in TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>())
{
var value = (property.GetValue(obj) ?? "").ToString();
queryString.Add(property.Name, value);
}
return queryString.ToString();
}
}
A quick extension method based version:
class Program
{
static void Main(string[] args)
{
var parameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("A", "AValue"),
new KeyValuePair<string, string>("B", "BValue")
};
string output = "?" + string.Join("&", parameters.ConvertAll(param => param.ToQueryString()).ToArray());
}
}
public static class KeyValueExtensions
{
public static string ToQueryString(this KeyValuePair<string, string> obj)
{
return obj.Key + "=" + HttpUtility.UrlEncode(obj.Value);
}
}
You could use a where clause to select which parameters get added to the string.
There's lots of good answers here but for those using modern C# this may be a nice utility class to keep around.
public class QueryParamBuilder
{
private readonly Dictionary<string, string> _fields = new();
public QueryParamBuilder Add(string key, string value)
{
_fields.Add(key, value);
return this;
}
public string Build()
{
return $"?{String.Join("&", _fields.Select(pair => $"{HttpUtility.UrlEncode(pair.Key)}={HttpUtility.UrlEncode(pair.Value)}"))}";
}
public static QueryParamBuilder New => new();
}
I use an internal Dictionary here because dictionaries are enumerable key value pairs internally which makes iterating over them much easier than a NameValueCollection.
Then the query string itself is a simple interpolated string with a join.
Additionally I provide a static interface into the constructor to make the construction of a new builder very easy and only allow one exposed method Add to add new query parameter values. Finally you terminate the chain with Build() to actually get the final string.
Here's an example of its usage
var queryString = QueryParamBuilder.New
.Add("id", "0123")
.Add("value2", 1234.ToString())
.Add("valueWithSpace","value with spa12!###!ce")
.Build();
The result is as expected
?id=0123&value2=1234&valueWithSpace=value+with+spa12!%40%23%40!ce
Hopefully some of you will find this nice and elegant.
Assuming that you want to reduce dependencies to other assemblies and to keep things simple, you can do:
var sb = new System.Text.StringBuilder();
sb.Append("a=" + HttpUtility.UrlEncode("TheValueOfA") + "&");
sb.Append("b=" + HttpUtility.UrlEncode("TheValueOfB") + "&");
sb.Append("c=" + HttpUtility.UrlEncode("TheValueOfC") + "&");
sb.Append("d=" + HttpUtility.UrlEncode("TheValueOfD") + "&");
sb.Remove(sb.Length-1, 1); // Remove the final '&'
string result = sb.ToString();
This works well with loops too. The final ampersand removal needs to go outside of the loop.
Note that the concatenation operator is used to improve readability. The cost of using it compared to the cost of using a StringBuilder is minimal (I think Jeff Atwood posted something on this topic).
I have an extension method for Uri that:
Accepts anonymous objects: uri.WithQuery(new { name = "value" })
Accepts collections of string/string pairs (e.g. Dictionary`2).
Accepts collections of string/object pairs (e.g. RouteValueDictionary).
Accepts NameValueCollections.
Sorts the query values by key so the same values produce equal URIs.
Supports multiple values per key, preserving their original order.
The documented version can be found here.
The extension:
public static Uri WithQuery(this Uri uri, object values)
{
if (uri == null)
throw new ArgumentNullException(nameof(uri));
if (values != null)
{
var query = string.Join(
"&", from p in ParseQueryValues(values)
where !string.IsNullOrWhiteSpace(p.Key)
let k = HttpUtility.UrlEncode(p.Key.Trim())
let v = HttpUtility.UrlEncode(p.Value)
orderby k
select string.IsNullOrEmpty(v) ? k : $"{k}={v}");
if (query.Length != 0 || uri.Query.Length != 0)
uri = new UriBuilder(uri) { Query = query }.Uri;
}
return uri;
}
The query parser:
private static IEnumerable<KeyValuePair<string, string>> ParseQueryValues(object values)
{
// Check if a name/value collection.
var nvc = values as NameValueCollection;
if (nvc != null)
return from key in nvc.AllKeys
from val in nvc.GetValues(key)
select new KeyValuePair<string, string>(key, val);
// Check if a string/string dictionary.
var ssd = values as IEnumerable<KeyValuePair<string, string>>;
if (ssd != null)
return ssd;
// Check if a string/object dictionary.
var sod = values as IEnumerable<KeyValuePair<string, object>>;
if (sod == null)
{
// Check if a non-generic dictionary.
var ngd = values as IDictionary;
if (ngd != null)
sod = ngd.Cast<dynamic>().ToDictionary<dynamic, string, object>(
p => p.Key.ToString(), p => p.Value as object);
// Convert object properties to dictionary.
if (sod == null)
sod = new RouteValueDictionary(values);
}
// Normalize and return the values.
return from pair in sod
from val in pair.Value as IEnumerable<string>
?? new[] { pair.Value?.ToString() }
select new KeyValuePair<string, string>(pair.Key, val);
}
Here are the tests:
var uri = new Uri("https://stackoverflow.com/yo?oldKey=oldValue");
// Test with a string/string dictionary.
var q = uri.WithQuery(new Dictionary<string, string>
{
["k1"] = string.Empty,
["k2"] = null,
["k3"] = "v3"
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1&k2&k3=v3"));
// Test with a string/object dictionary.
q = uri.WithQuery(new Dictionary<string, object>
{
["k1"] = "v1",
["k2"] = new[] { "v2a", "v2b" },
["k3"] = null
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3"));
// Test with a name/value collection.
var nvc = new NameValueCollection()
{
["k1"] = string.Empty,
["k2"] = "v2a"
};
nvc.Add("k2", "v2b");
q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1&k2=v2a&k2=v2b"));
// Test with any dictionary.
q = uri.WithQuery(new Dictionary<int, HashSet<string>>
{
[1] = new HashSet<string> { "v1" },
[2] = new HashSet<string> { "v2a", "v2b" },
[3] = null
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?1=v1&2=v2a&2=v2b&3"));
// Test with an anonymous object.
q = uri.WithQuery(new
{
k1 = "v1",
k2 = new[] { "v2a", "v2b" },
k3 = new List<string> { "v3" },
k4 = true,
k5 = null as Queue<string>
});
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3=v3&k4=True&k5"));
// Keep existing query using a name/value collection.
nvc = HttpUtility.ParseQueryString(uri.Query);
nvc.Add("newKey", "newValue");
q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?newKey=newValue&oldKey=oldValue"));
// Merge two query objects using the RouteValueDictionary.
var an1 = new { k1 = "v1" };
var an2 = new { k2 = "v2" };
q = uri.WithQuery(
new RouteValueDictionary(an1).Concat(
new RouteValueDictionary(an2)));
Debug.Assert(q == new Uri(
"https://stackoverflow.com/yo?k1=v1&k2=v2"));
Chain-able wrapper class for HttpValueCollection:
namespace System.Web.Mvc {
public class QueryStringBuilder {
private NameValueCollection collection;
public QueryStringBuilder() {
collection = System.Web.HttpUtility.ParseQueryString(string.Empty);
}
public QueryStringBuilder Add(string key, string value) {
collection.Add(key, value);
return this;
}
public QueryStringBuilder Remove(string key) {
collection.Remove(key);
return this;
}
public string this[string key] {
get { return collection[key]; }
set { collection[key] = value; }
}
public string ToString() {
return collection.ToString();
}
}
}
Example usage:
QueryStringBuilder parameters = new QueryStringBuilder()
.Add("view", ViewBag.PageView)
.Add("page", ViewBag.PageNumber)
.Add("size", ViewBag.PageSize);
string queryString = parameters.ToString();
Same as accepted solution, but transfred to "dot" LINQ syntax...
private string ToQueryString(NameValueCollection nvc)
{
if (nvc == null) return String.Empty;
var queryParams =
string.Join("&", nvc.AllKeys.Select(key =>
string.Join("&", nvc.GetValues(key).Select(v => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(v))))));
return "?" + queryParams;
}
While not elegant, I opted for a simpler version that doesn't use NameValueCollecitons - just a builder pattern wrapped around StringBuilder.
public class UrlBuilder
{
#region Variables / Properties
private readonly StringBuilder _builder;
#endregion Variables / Properties
#region Constructor
public UrlBuilder(string urlBase)
{
_builder = new StringBuilder(urlBase);
}
#endregion Constructor
#region Methods
public UrlBuilder AppendParameter(string paramName, string value)
{
if (_builder.ToString().Contains("?"))
_builder.Append("&");
else
_builder.Append("?");
_builder.Append(HttpUtility.UrlEncode(paramName));
_builder.Append("=");
_builder.Append(HttpUtility.UrlEncode(value));
return this;
}
public override string ToString()
{
return _builder.ToString();
}
#endregion Methods
}
Per existing answers, I made sure to use HttpUtility.UrlEncode calls. It's used like so:
string url = new UrlBuilder("http://www.somedomain.com/")
.AppendParameter("a", "true")
.AppendParameter("b", "muffin")
.AppendParameter("c", "muffin button")
.ToString();
// Result: http://www.somedomain.com?a=true&b=muffin&c=muffin%20button
The query string can be added to a URL by:
create a name value collection object
add the query string items and their values to this object
encode this name value collection object to the url the code is provided in the below link
https://blog.codingnovice.com/blog
public ActionResult Create()
{
//declaring name value collection object
NameValueCollection collection = new NameValueCollection();
//adding new value to the name value collection object
collection.Add("Id1", "wwe323");
collection.Add("Id2", "454w");
collection.Add("Id3", "tyt5656");
collection.Add("Id4", "343wdsd");
//generating query string
string url = GenerateQueryString(collection);
return View();
}
private string GenerateQueryString(NameValueCollection collection)
{
var querystring = (
from key in collection.AllKeys
from value in collection.GetValues(key)
select string.Format("{0}={1}",
HttpUtility.UrlEncode(key),
HttpUtility.UrlEncode(value))
).ToArray();
return "?" + string.Join("&", querystring);
}
I added the following method to my PageBase class.
protected void Redirect(string url)
{
Response.Redirect(url);
}
protected void Redirect(string url, NameValueCollection querystrings)
{
StringBuilder redirectUrl = new StringBuilder(url);
if (querystrings != null)
{
for (int index = 0; index < querystrings.Count; index++)
{
if (index == 0)
{
redirectUrl.Append("?");
}
redirectUrl.Append(querystrings.Keys[index]);
redirectUrl.Append("=");
redirectUrl.Append(HttpUtility.UrlEncode(querystrings[index]));
if (index < querystrings.Count - 1)
{
redirectUrl.Append("&");
}
}
}
this.Redirect(redirectUrl.ToString());
}
To call:
NameValueCollection querystrings = new NameValueCollection();
querystrings.Add("language", "en");
querystrings.Add("id", "134");
this.Redirect("http://www.mypage.com", querystrings);
I wrote some extension methods that I have found very useful when working with QueryStrings. Often I want to start with the current QueryString and modify before using it. Something like,
var res = Request.QueryString.Duplicate()
.ChangeField("field1", "somevalue")
.ChangeField("field2", "only if following is true", true)
.ChangeField("id", id, id>0)
.WriteLocalPathWithQuery(Request.Url)); //Uses context to write the path
For more and the source: http://www.charlesrcook.com/archive/2008/07/23/c-extension-methods-for-asp.net-query-string-operations.aspx
It's basic, but I like the style.
Just wanted to throw in my 2 cents:
public static class HttpClientExt
{
public static Uri AddQueryParams(this Uri uri, string query)
{
var ub = new UriBuilder(uri);
ub.Query = string.IsNullOrEmpty(uri.Query) ? query : string.Join("&", uri.Query.Substring(1), query);
return ub.Uri;
}
public static Uri AddQueryParams(this Uri uri, IEnumerable<string> query)
{
return uri.AddQueryParams(string.Join("&", query));
}
public static Uri AddQueryParams(this Uri uri, string key, string value)
{
return uri.AddQueryParams(string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)));
}
public static Uri AddQueryParams(this Uri uri, params KeyValuePair<string,string>[] kvps)
{
return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
}
public static Uri AddQueryParams(this Uri uri, IDictionary<string, string> kvps)
{
return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
}
public static Uri AddQueryParams(this Uri uri, NameValueCollection nvc)
{
return uri.AddQueryParams(nvc.AllKeys.SelectMany(nvc.GetValues, (key, value) => string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))));
}
}
The docs say that uri.Query will start with a ? if it's non-empty and you should trim it off if you're going to modify it.
Note that HttpUtility.UrlEncode is found in System.Web.
Usage:
var uri = new Uri("https://api.del.icio.us/v1/posts/suggest").AddQueryParam("url","http://stackoverflow.com")
// USAGE
[TestMethod]
public void TestUrlBuilder()
{
Console.WriteLine(
new UrlBuilder("http://www.google.com?A=B")
.AddPath("SomePathName")
.AddPath("AnotherPathName")
.SetQuery("SomeQueryKey", "SomeQueryValue")
.AlterQuery("A", x => x + "C"));
}
Output:
http://www.google.com/SomePathName/AnotherPathName?A=BC&SomeQueryKey=SomeQueryValue
The code; you can all thank me somewhere, somehow :D
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
// By Demetris Leptos
namespace TheOperator.Foundation.Web
{
public class UrlBuilder
{
public string Scheme { get; set; }
public string Host { get; set; }
public int? Port { get; set; }
public List<string> Paths { get; set; }
public SortedDictionary<string, string> QueryPairs { get; set; }
public UrlBuilder(string url)
{
this.Paths = new List<string>();
this.QueryPairs = new SortedDictionary<string, string>();
string path = null;
string query = null;
Uri relativeUri = null;
if (!Uri.TryCreate(url, UriKind.Relative, out relativeUri))
{
var uriBuilder = new UriBuilder(url);
this.Scheme = uriBuilder.Scheme;
this.Host = uriBuilder.Host;
this.Port = uriBuilder.Port;
path = uriBuilder.Path;
query = uriBuilder.Query;
}
else
{
var queryIndex = url.IndexOf('?');
if (queryIndex >= 0)
{
path = url.Substring(0, queryIndex);
query = url.Substring(queryIndex + 1);
}
else
{
path = url;
}
}
this.Paths.AddRange(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));
if (query != null)
{
var queryKeyValuePairs = HttpUtility.ParseQueryString(query);
foreach (var queryKey in queryKeyValuePairs.AllKeys)
{
this.QueryPairs[queryKey] = queryKeyValuePairs[queryKey];
}
}
}
public UrlBuilder AddPath(string value)
{
this.Paths.Add(value);
return this;
}
public UrlBuilder SetQuery(string key, string value)
{
this.QueryPairs[key] = value;
return this;
}
public UrlBuilder RemoveQuery(string key)
{
this.QueryPairs.Remove(key);
return this;
}
public UrlBuilder AlterQuery(string key, Func<string, string> alterMethod, bool removeOnNull = false)
{
string value;
this.QueryPairs.TryGetValue(key, out value);
value = alterMethod(value);
if (removeOnNull && value == null)
{
return this.RemoveQuery(key);
}
else
{
return this.SetQuery(key, value);
}
}
public override string ToString()
{
var path = !string.IsNullOrWhiteSpace(this.Host)
? string.Join("/", this.Host, string.Join("/", this.Paths))
: string.Join("/", this.Paths);
var query = string.Join("&", this.QueryPairs.Select(x => string.Concat(x.Key, "=", HttpUtility.UrlEncode(x.Value))));
return string.Concat(
!string.IsNullOrWhiteSpace(this.Scheme) ? string.Concat(this.Scheme, "://") : null,
path,
!string.IsNullOrWhiteSpace(query) ? string.Concat("?", query) : null);
}
}
}
I went with the solution proposed by DSO (answered on Aug 2 '11 at 7:29), his solution does not require using HttpUtility. However, as per an article posted in Dotnetpearls, using a Dictionary is faster (in performance) than using NameValueCollection. Here is DSO's solution modified to use Dictionary in place of NameValueCollection.
public static Dictionary<string, string> QueryParametersDictionary()
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("name", "John Doe");
dictionary.Add("address.city", "Seattle");
dictionary.Add("address.state_code", "WA");
dictionary.Add("api_key", "5352345263456345635");
return dictionary;
}
public static string ToQueryString(Dictionary<string, string> nvc)
{
StringBuilder sb = new StringBuilder();
bool first = true;
foreach (KeyValuePair<string, string> pair in nvc)
{
if (!first)
{
sb.Append("&");
}
sb.AppendFormat("{0}={1}", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value));
first = false;
}
return sb.ToString();
}
I wrote a helper for my razor project using some of the hints from other answers.
The ParseQueryString business is necessary because we are not allowed to tamper with the QueryString object of the current request.
#helper GetQueryStringWithValue(string key, string value) {
var queryString = System.Web.HttpUtility.ParseQueryString(HttpContext.Current.Request.QueryString.ToString());
queryString[key] = value;
#Html.Raw(queryString.ToString())
}
I use it like this:
location.search = '?#Helpers.GetQueryStringWithValue("var-name", "var-value")';
If you want it to take more than one value, just change the parameters to a Dictionary and add the pairs to the query string.
Related
Convert query string from VBA to C# and send it with HttpClient [duplicate]
A common task when calling web resources from a code is building a query string to including all the necessary parameters. While by all means no rocket science, there are some nifty details you need to take care of like, appending an & if not the first parameter, encoding the parameters etc. The code to do it is very simple, but a bit tedious: StringBuilder SB = new StringBuilder(); if (NeedsToAddParameter A) { SB.Append("A="); SB.Append(HttpUtility.UrlEncode("TheValueOfA")); } if (NeedsToAddParameter B) { if (SB.Length>0) SB.Append("&"); SB.Append("B="); SB.Append(HttpUtility.UrlEncode("TheValueOfB")); } } This is such a common task one would expect a utility class to exist that makes it more elegant and readable. Scanning MSDN, I failed to find one—which brings me to the following question: What is the most elegant clean way you know of doing the above?
You can create a new writeable instance of HttpValueCollection by calling System.Web.HttpUtility.ParseQueryString(string.Empty), and then use it as any NameValueCollection. Once you have added the values you want, you can call ToString on the collection to get a query string, as follows: NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty); queryString.Add("key1", "value1"); queryString.Add("key2", "value2"); return queryString.ToString(); // Returns "key1=value1&key2=value2", all URL-encoded The HttpValueCollection is internal and so you cannot directly construct an instance. However, once you obtain an instance you can use it like any other NameValueCollection. Since the actual object you are working with is an HttpValueCollection, calling ToString method will call the overridden method on HttpValueCollection, which formats the collection as a URL-encoded query string. After searching SO and the web for an answer to a similar issue, this is the most simple solution I could find. .NET Core If you're working in .NET Core, you can use the Microsoft.AspNetCore.WebUtilities.QueryHelpers class, which simplifies this greatly. https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers Sample Code: const string url = "https://customer-information.azure-api.net/customers/search/taxnbr"; var param = new Dictionary<string, string>() { { "CIKey", "123456789" } }; var newUrl = new Uri(QueryHelpers.AddQueryString(url, param));
If you look under the hood the QueryString property is a NameValueCollection. When I've done similar things I've usually been interested in serialising AND deserialising so my suggestion is to build a NameValueCollection up and then pass to: using System.Linq; using System.Web; using System.Collections.Specialized; private string ToQueryString(NameValueCollection nvc) { var array = ( from key in nvc.AllKeys from value in nvc.GetValues(key) select string.Format( "{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)) ).ToArray(); return "?" + string.Join("&", array); } I imagine there's a super elegant way to do this in LINQ too...
With the inspiration from Roy Tinker's comment, I ended up using a simple extension method on the Uri class that keeps my code concise and clean: using System.Web; public static class HttpExtensions { public static Uri AddQuery(this Uri uri, string name, string value) { var httpValueCollection = HttpUtility.ParseQueryString(uri.Query); httpValueCollection.Remove(name); httpValueCollection.Add(name, value); var ub = new UriBuilder(uri); ub.Query = httpValueCollection.ToString(); return ub.Uri; } } Usage: Uri url = new Uri("http://localhost/rest/something/browse"). AddQuery("page", "0"). AddQuery("pageSize", "200"); Edit - Standards compliant variant As several people pointed out, httpValueCollection.ToString() encodes Unicode characters in a non-standards-compliant way. This is a variant of the same extension method that handles such characters by invoking HttpUtility.UrlEncode method instead of the deprecated HttpUtility.UrlEncodeUnicode method. using System.Web; public static Uri AddQuery(this Uri uri, string name, string value) { var httpValueCollection = HttpUtility.ParseQueryString(uri.Query); httpValueCollection.Remove(name); httpValueCollection.Add(name, value); var ub = new UriBuilder(uri); // this code block is taken from httpValueCollection.ToString() method // and modified so it encodes strings with HttpUtility.UrlEncode if (httpValueCollection.Count == 0) ub.Query = String.Empty; else { var sb = new StringBuilder(); for (int i = 0; i < httpValueCollection.Count; i++) { string text = httpValueCollection.GetKey(i); { text = HttpUtility.UrlEncode(text); string val = (text != null) ? (text + "=") : string.Empty; string[] vals = httpValueCollection.GetValues(i); if (sb.Length > 0) sb.Append('&'); if (vals == null || vals.Length == 0) sb.Append(val); else { if (vals.Length == 1) { sb.Append(val); sb.Append(HttpUtility.UrlEncode(vals[0])); } else { for (int j = 0; j < vals.Length; j++) { if (j > 0) sb.Append('&'); sb.Append(val); sb.Append(HttpUtility.UrlEncode(vals[j])); } } } } } ub.Query = sb.ToString(); } return ub.Uri; }
Flurl [disclosure: I'm the author] supports building query strings via anonymous objects (among other ways): var url = "http://www.some-api.com".SetQueryParams(new { api_key = ConfigurationManager.AppSettings["SomeApiKey"], max_results = 20, q = "Don't worry, I'll get encoded!" }); The optional Flurl.Http companion lib allows you to do HTTP calls right off the same fluent call chain, extending it into a full-blown REST client: T result = await "https://api.mysite.com" .AppendPathSegment("person") .SetQueryParams(new { ap_key = "my-key" }) .WithOAuthBearerToken("MyToken") .PostJsonAsync(new { first_name = firstName, last_name = lastName }) .ReceiveJson<T>(); The full package is available on NuGet: PM> Install-Package Flurl.Http or just the stand-alone URL builder: PM> Install-Package Flurl
Curious that no one has mentioned QueryBuilder from AspNet.Core. It's helpful when you have a query with duplicate key like ?filter=a&filter=b var qb = new QueryBuilder(); qb.Add("filter", new string[] {"A", "B"}); Then you'll just add qb to the URI, it is converted automatically to string. https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.extensions.querybuilder?view=aspnetcore-5.0
I answered a similar question a while ago. Basically, the best way would be to use the class HttpValueCollection, which ASP.NET's Request.QueryString property actually is, unfortunately it is internal in the .NET framework. You could use Reflector to grab it (and place it into your Utils class). This way you could manipulate the query string like a NameValueCollection, but with all the url encoding/decoding issues taken care for you. HttpValueCollection extends NameValueCollection, and has a constructor that takes an encoded query string (ampersands and question marks included), and it overrides a ToString() method to later rebuild the query string from the underlying collection. Example: var coll = new HttpValueCollection(); coll["userId"] = "50"; coll["paramA"] = "A"; coll["paramB"] = "B"; string query = coll.ToString(true); // true means use urlencode Console.WriteLine(query); // prints: userId=50¶mA=A¶mB=B
Here's a fluent/lambda-ish way as an extension method (combining concepts in previous posts) that supports multiple values for the same key. My personal preference is extensions over wrappers for discover-ability by other team members for stuff like this. Note that there's controversy around encoding methods, plenty of posts about it on Stack Overflow (one such post) and MSDN bloggers (like this one). public static string ToQueryString(this NameValueCollection source) { return String.Join("&", source.AllKeys .SelectMany(key => source.GetValues(key) .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)))) .ToArray()); } edit: with null support, though you'll probably need to adapt it for your particular situation public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries) { return source != null ? String.Join("&", source.AllKeys .Where(key => !removeEmptyEntries || source.GetValues(key) .Where(value => !String.IsNullOrEmpty(value)) .Any()) .SelectMany(key => source.GetValues(key) .Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value)) .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty))) .ToArray()) : string.Empty; }
Here's my late entry. I didn't like any of the others for various reasons, so I wrote my own. This version features: Use of StringBuilder only. No ToArray() calls or other extension methods. It doesn't look as pretty as some of the other responses, but I consider this a core function so efficiency is more important than having "fluent", "one-liner" code which hide inefficiencies. Handles multiple values per key. (Didn't need it myself but just to silence Mauricio ;) public string ToQueryString(NameValueCollection nvc) { StringBuilder sb = new StringBuilder("?"); bool first = true; foreach (string key in nvc.AllKeys) { foreach (string value in nvc.GetValues(key)) { if (!first) { sb.Append("&"); } sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value)); first = false; } } return sb.ToString(); } Example Usage var queryParams = new NameValueCollection() { { "x", "1" }, { "y", "2" }, { "foo", "bar" }, { "foo", "baz" }, { "special chars", "? = &" }, }; string url = "http://example.com/stuff" + ToQueryString(queryParams); Console.WriteLine(url); Output http://example.com/stuff?x=1&y=2&foo=bar&foo=baz&special%20chars=%3F%20%3D%20%26
I needed to solve the same problem for a portable class library (PCL) that I'm working on. In this case, I don't have access to System.Web so I can't use ParseQueryString. Instead I used System.Net.Http.FormUrlEncodedContent like so: var url = new UriBuilder("http://example.com"); url.Query = new FormUrlEncodedContent(new Dictionary<string,string>() { {"param1", "val1"}, {"param2", "val2"}, {"param3", "val3"}, }).ReadAsStringAsync().Result;
How about creating extension methods that allow you to add the parameters in a fluent style like this? string a = "http://www.somedomain.com/somepage.html" .AddQueryParam("A", "TheValueOfA") .AddQueryParam("B", "TheValueOfB") .AddQueryParam("Z", "TheValueOfZ"); string b = new StringBuilder("http://www.somedomain.com/anotherpage.html") .AddQueryParam("A", "TheValueOfA") .AddQueryParam("B", "TheValueOfB") .AddQueryParam("Z", "TheValueOfZ") .ToString(); Here's the overload that uses a string: public static string AddQueryParam( this string source, string key, string value) { string delim; if ((source == null) || !source.Contains("?")) { delim = "?"; } else if (source.EndsWith("?") || source.EndsWith("&")) { delim = string.Empty; } else { delim = "&"; } return source + delim + HttpUtility.UrlEncode(key) + "=" + HttpUtility.UrlEncode(value); } And here's the overload that uses a StringBuilder: public static StringBuilder AddQueryParam( this StringBuilder source, string key, string value) { bool hasQuery = false; for (int i = 0; i < source.Length; i++) { if (source[i] == '?') { hasQuery = true; break; } } string delim; if (!hasQuery) { delim = "?"; } else if ((source[source.Length - 1] == '?') || (source[source.Length - 1] == '&')) { delim = string.Empty; } else { delim = "&"; } return source.Append(delim).Append(HttpUtility.UrlEncode(key)) .Append("=").Append(HttpUtility.UrlEncode(value)); }
public static string ToQueryString(this Dictionary<string, string> source) { return String.Join("&", source.Select(kvp => String.Format("{0}={1}", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))).ToArray()); } public static string ToQueryString(this NameValueCollection source) { return String.Join("&", source.Cast<string>().Select(key => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(source[key]))).ToArray()); }
Add this class to your project using System; using System.Collections.Generic; using System.Linq; using System.Web; public class QueryStringBuilder { private readonly List<KeyValuePair<string, object>> _list; public QueryStringBuilder() { _list = new List<KeyValuePair<string, object>>(); } public void Add(string name, object value) { _list.Add(new KeyValuePair<string, object>(name, value)); } public override string ToString() { return String.Join("&", _list.Select(kvp => String.Concat(Uri.EscapeDataString(kvp.Key), "=", Uri.EscapeDataString(kvp.Value.ToString())))); } } And use it like this: var actual = new QueryStringBuilder { {"foo", 123}, {"bar", "val31"}, {"bar", "val32"} }; actual.Add("a+b", "c+d"); actual.ToString(); // "foo=123&bar=val31&bar=val32&a%2bb=c%2bd"
Untested, but I think something along these lines would work quite nicely public class QueryString { private Dictionary<string,string> _Params = new Dictionary<string,string>(); public overide ToString() { List<string> returnParams = new List<string>(); foreach (KeyValuePair param in _Params) { returnParams.Add(String.Format("{0}={1}", param.Key, param.Value)); } // return String.Format("?{0}", String.Join("&", returnParams.ToArray())); // credit annakata return "?" + String.Join("&", returnParams.ToArray()); } public void Add(string key, string value) { _Params.Add(key, HttpUtility.UrlEncode(value)); } } QueryString query = new QueryString(); query.Add("param1", "value1"); query.Add("param2", "value2"); return query.ToString();
My offering: public static Uri AddQuery(this Uri uri, string name, string value) { // this actually returns HttpValueCollection : NameValueCollection // which uses unicode compliant encoding on ToString() var query = HttpUtility.ParseQueryString(uri.Query); query.Add(name, value); var uriBuilder = new UriBuilder(uri) { Query = query.ToString() }; return uriBuilder.Uri; } Usage: var uri = new Uri("http://stackoverflow.com").AddQuery("such", "method") .AddQuery("wow", "soFluent"); // http://stackoverflow.com?such=method&wow=soFluent
In dotnet core QueryHelpers.AddQueryString() will accept an IDictionary<string,string> of key-value pairs. To save a few memory allocs and CPU cycles you can use SortedList<,> instead of Dictionary<,>, with an appropriate capacity and items added in sort order... var queryParams = new SortedList<string,string>(2); queryParams.Add("abc", "val1"); queryParams.Add("def", "val2"); string requestUri = QueryHelpers.AddQueryString("https://localhost/api", queryParams);
Combined the top answers to create an anonymous object version: var queryString = HttpUtility2.BuildQueryString(new { key2 = "value2", key1 = "value1", }); That generates this: key2=value2&key1=value1 Here's the code: public static class HttpUtility2 { public static string BuildQueryString<T>(T obj) { var queryString = HttpUtility.ParseQueryString(string.Empty); foreach (var property in TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>()) { var value = (property.GetValue(obj) ?? "").ToString(); queryString.Add(property.Name, value); } return queryString.ToString(); } }
A quick extension method based version: class Program { static void Main(string[] args) { var parameters = new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("A", "AValue"), new KeyValuePair<string, string>("B", "BValue") }; string output = "?" + string.Join("&", parameters.ConvertAll(param => param.ToQueryString()).ToArray()); } } public static class KeyValueExtensions { public static string ToQueryString(this KeyValuePair<string, string> obj) { return obj.Key + "=" + HttpUtility.UrlEncode(obj.Value); } } You could use a where clause to select which parameters get added to the string.
There's lots of good answers here but for those using modern C# this may be a nice utility class to keep around. public class QueryParamBuilder { private readonly Dictionary<string, string> _fields = new(); public QueryParamBuilder Add(string key, string value) { _fields.Add(key, value); return this; } public string Build() { return $"?{String.Join("&", _fields.Select(pair => $"{HttpUtility.UrlEncode(pair.Key)}={HttpUtility.UrlEncode(pair.Value)}"))}"; } public static QueryParamBuilder New => new(); } I use an internal Dictionary here because dictionaries are enumerable key value pairs internally which makes iterating over them much easier than a NameValueCollection. Then the query string itself is a simple interpolated string with a join. Additionally I provide a static interface into the constructor to make the construction of a new builder very easy and only allow one exposed method Add to add new query parameter values. Finally you terminate the chain with Build() to actually get the final string. Here's an example of its usage var queryString = QueryParamBuilder.New .Add("id", "0123") .Add("value2", 1234.ToString()) .Add("valueWithSpace","value with spa12!###!ce") .Build(); The result is as expected ?id=0123&value2=1234&valueWithSpace=value+with+spa12!%40%23%40!ce Hopefully some of you will find this nice and elegant.
Assuming that you want to reduce dependencies to other assemblies and to keep things simple, you can do: var sb = new System.Text.StringBuilder(); sb.Append("a=" + HttpUtility.UrlEncode("TheValueOfA") + "&"); sb.Append("b=" + HttpUtility.UrlEncode("TheValueOfB") + "&"); sb.Append("c=" + HttpUtility.UrlEncode("TheValueOfC") + "&"); sb.Append("d=" + HttpUtility.UrlEncode("TheValueOfD") + "&"); sb.Remove(sb.Length-1, 1); // Remove the final '&' string result = sb.ToString(); This works well with loops too. The final ampersand removal needs to go outside of the loop. Note that the concatenation operator is used to improve readability. The cost of using it compared to the cost of using a StringBuilder is minimal (I think Jeff Atwood posted something on this topic).
I have an extension method for Uri that: Accepts anonymous objects: uri.WithQuery(new { name = "value" }) Accepts collections of string/string pairs (e.g. Dictionary`2). Accepts collections of string/object pairs (e.g. RouteValueDictionary). Accepts NameValueCollections. Sorts the query values by key so the same values produce equal URIs. Supports multiple values per key, preserving their original order. The documented version can be found here. The extension: public static Uri WithQuery(this Uri uri, object values) { if (uri == null) throw new ArgumentNullException(nameof(uri)); if (values != null) { var query = string.Join( "&", from p in ParseQueryValues(values) where !string.IsNullOrWhiteSpace(p.Key) let k = HttpUtility.UrlEncode(p.Key.Trim()) let v = HttpUtility.UrlEncode(p.Value) orderby k select string.IsNullOrEmpty(v) ? k : $"{k}={v}"); if (query.Length != 0 || uri.Query.Length != 0) uri = new UriBuilder(uri) { Query = query }.Uri; } return uri; } The query parser: private static IEnumerable<KeyValuePair<string, string>> ParseQueryValues(object values) { // Check if a name/value collection. var nvc = values as NameValueCollection; if (nvc != null) return from key in nvc.AllKeys from val in nvc.GetValues(key) select new KeyValuePair<string, string>(key, val); // Check if a string/string dictionary. var ssd = values as IEnumerable<KeyValuePair<string, string>>; if (ssd != null) return ssd; // Check if a string/object dictionary. var sod = values as IEnumerable<KeyValuePair<string, object>>; if (sod == null) { // Check if a non-generic dictionary. var ngd = values as IDictionary; if (ngd != null) sod = ngd.Cast<dynamic>().ToDictionary<dynamic, string, object>( p => p.Key.ToString(), p => p.Value as object); // Convert object properties to dictionary. if (sod == null) sod = new RouteValueDictionary(values); } // Normalize and return the values. return from pair in sod from val in pair.Value as IEnumerable<string> ?? new[] { pair.Value?.ToString() } select new KeyValuePair<string, string>(pair.Key, val); } Here are the tests: var uri = new Uri("https://stackoverflow.com/yo?oldKey=oldValue"); // Test with a string/string dictionary. var q = uri.WithQuery(new Dictionary<string, string> { ["k1"] = string.Empty, ["k2"] = null, ["k3"] = "v3" }); Debug.Assert(q == new Uri( "https://stackoverflow.com/yo?k1&k2&k3=v3")); // Test with a string/object dictionary. q = uri.WithQuery(new Dictionary<string, object> { ["k1"] = "v1", ["k2"] = new[] { "v2a", "v2b" }, ["k3"] = null }); Debug.Assert(q == new Uri( "https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3")); // Test with a name/value collection. var nvc = new NameValueCollection() { ["k1"] = string.Empty, ["k2"] = "v2a" }; nvc.Add("k2", "v2b"); q = uri.WithQuery(nvc); Debug.Assert(q == new Uri( "https://stackoverflow.com/yo?k1&k2=v2a&k2=v2b")); // Test with any dictionary. q = uri.WithQuery(new Dictionary<int, HashSet<string>> { [1] = new HashSet<string> { "v1" }, [2] = new HashSet<string> { "v2a", "v2b" }, [3] = null }); Debug.Assert(q == new Uri( "https://stackoverflow.com/yo?1=v1&2=v2a&2=v2b&3")); // Test with an anonymous object. q = uri.WithQuery(new { k1 = "v1", k2 = new[] { "v2a", "v2b" }, k3 = new List<string> { "v3" }, k4 = true, k5 = null as Queue<string> }); Debug.Assert(q == new Uri( "https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3=v3&k4=True&k5")); // Keep existing query using a name/value collection. nvc = HttpUtility.ParseQueryString(uri.Query); nvc.Add("newKey", "newValue"); q = uri.WithQuery(nvc); Debug.Assert(q == new Uri( "https://stackoverflow.com/yo?newKey=newValue&oldKey=oldValue")); // Merge two query objects using the RouteValueDictionary. var an1 = new { k1 = "v1" }; var an2 = new { k2 = "v2" }; q = uri.WithQuery( new RouteValueDictionary(an1).Concat( new RouteValueDictionary(an2))); Debug.Assert(q == new Uri( "https://stackoverflow.com/yo?k1=v1&k2=v2"));
Chain-able wrapper class for HttpValueCollection: namespace System.Web.Mvc { public class QueryStringBuilder { private NameValueCollection collection; public QueryStringBuilder() { collection = System.Web.HttpUtility.ParseQueryString(string.Empty); } public QueryStringBuilder Add(string key, string value) { collection.Add(key, value); return this; } public QueryStringBuilder Remove(string key) { collection.Remove(key); return this; } public string this[string key] { get { return collection[key]; } set { collection[key] = value; } } public string ToString() { return collection.ToString(); } } } Example usage: QueryStringBuilder parameters = new QueryStringBuilder() .Add("view", ViewBag.PageView) .Add("page", ViewBag.PageNumber) .Add("size", ViewBag.PageSize); string queryString = parameters.ToString();
Same as accepted solution, but transfred to "dot" LINQ syntax... private string ToQueryString(NameValueCollection nvc) { if (nvc == null) return String.Empty; var queryParams = string.Join("&", nvc.AllKeys.Select(key => string.Join("&", nvc.GetValues(key).Select(v => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(v)))))); return "?" + queryParams; }
While not elegant, I opted for a simpler version that doesn't use NameValueCollecitons - just a builder pattern wrapped around StringBuilder. public class UrlBuilder { #region Variables / Properties private readonly StringBuilder _builder; #endregion Variables / Properties #region Constructor public UrlBuilder(string urlBase) { _builder = new StringBuilder(urlBase); } #endregion Constructor #region Methods public UrlBuilder AppendParameter(string paramName, string value) { if (_builder.ToString().Contains("?")) _builder.Append("&"); else _builder.Append("?"); _builder.Append(HttpUtility.UrlEncode(paramName)); _builder.Append("="); _builder.Append(HttpUtility.UrlEncode(value)); return this; } public override string ToString() { return _builder.ToString(); } #endregion Methods } Per existing answers, I made sure to use HttpUtility.UrlEncode calls. It's used like so: string url = new UrlBuilder("http://www.somedomain.com/") .AppendParameter("a", "true") .AppendParameter("b", "muffin") .AppendParameter("c", "muffin button") .ToString(); // Result: http://www.somedomain.com?a=true&b=muffin&c=muffin%20button
The query string can be added to a URL by: create a name value collection object add the query string items and their values to this object encode this name value collection object to the url the code is provided in the below link https://blog.codingnovice.com/blog public ActionResult Create() { //declaring name value collection object NameValueCollection collection = new NameValueCollection(); //adding new value to the name value collection object collection.Add("Id1", "wwe323"); collection.Add("Id2", "454w"); collection.Add("Id3", "tyt5656"); collection.Add("Id4", "343wdsd"); //generating query string string url = GenerateQueryString(collection); return View(); } private string GenerateQueryString(NameValueCollection collection) { var querystring = ( from key in collection.AllKeys from value in collection.GetValues(key) select string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)) ).ToArray(); return "?" + string.Join("&", querystring); }
I added the following method to my PageBase class. protected void Redirect(string url) { Response.Redirect(url); } protected void Redirect(string url, NameValueCollection querystrings) { StringBuilder redirectUrl = new StringBuilder(url); if (querystrings != null) { for (int index = 0; index < querystrings.Count; index++) { if (index == 0) { redirectUrl.Append("?"); } redirectUrl.Append(querystrings.Keys[index]); redirectUrl.Append("="); redirectUrl.Append(HttpUtility.UrlEncode(querystrings[index])); if (index < querystrings.Count - 1) { redirectUrl.Append("&"); } } } this.Redirect(redirectUrl.ToString()); } To call: NameValueCollection querystrings = new NameValueCollection(); querystrings.Add("language", "en"); querystrings.Add("id", "134"); this.Redirect("http://www.mypage.com", querystrings);
I wrote some extension methods that I have found very useful when working with QueryStrings. Often I want to start with the current QueryString and modify before using it. Something like, var res = Request.QueryString.Duplicate() .ChangeField("field1", "somevalue") .ChangeField("field2", "only if following is true", true) .ChangeField("id", id, id>0) .WriteLocalPathWithQuery(Request.Url)); //Uses context to write the path For more and the source: http://www.charlesrcook.com/archive/2008/07/23/c-extension-methods-for-asp.net-query-string-operations.aspx It's basic, but I like the style.
Just wanted to throw in my 2 cents: public static class HttpClientExt { public static Uri AddQueryParams(this Uri uri, string query) { var ub = new UriBuilder(uri); ub.Query = string.IsNullOrEmpty(uri.Query) ? query : string.Join("&", uri.Query.Substring(1), query); return ub.Uri; } public static Uri AddQueryParams(this Uri uri, IEnumerable<string> query) { return uri.AddQueryParams(string.Join("&", query)); } public static Uri AddQueryParams(this Uri uri, string key, string value) { return uri.AddQueryParams(string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))); } public static Uri AddQueryParams(this Uri uri, params KeyValuePair<string,string>[] kvps) { return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value)))); } public static Uri AddQueryParams(this Uri uri, IDictionary<string, string> kvps) { return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value)))); } public static Uri AddQueryParams(this Uri uri, NameValueCollection nvc) { return uri.AddQueryParams(nvc.AllKeys.SelectMany(nvc.GetValues, (key, value) => string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)))); } } The docs say that uri.Query will start with a ? if it's non-empty and you should trim it off if you're going to modify it. Note that HttpUtility.UrlEncode is found in System.Web. Usage: var uri = new Uri("https://api.del.icio.us/v1/posts/suggest").AddQueryParam("url","http://stackoverflow.com")
// USAGE [TestMethod] public void TestUrlBuilder() { Console.WriteLine( new UrlBuilder("http://www.google.com?A=B") .AddPath("SomePathName") .AddPath("AnotherPathName") .SetQuery("SomeQueryKey", "SomeQueryValue") .AlterQuery("A", x => x + "C")); } Output: http://www.google.com/SomePathName/AnotherPathName?A=BC&SomeQueryKey=SomeQueryValue The code; you can all thank me somewhere, somehow :D using System; using System.Collections.Generic; using System.Linq; using System.Web; // By Demetris Leptos namespace TheOperator.Foundation.Web { public class UrlBuilder { public string Scheme { get; set; } public string Host { get; set; } public int? Port { get; set; } public List<string> Paths { get; set; } public SortedDictionary<string, string> QueryPairs { get; set; } public UrlBuilder(string url) { this.Paths = new List<string>(); this.QueryPairs = new SortedDictionary<string, string>(); string path = null; string query = null; Uri relativeUri = null; if (!Uri.TryCreate(url, UriKind.Relative, out relativeUri)) { var uriBuilder = new UriBuilder(url); this.Scheme = uriBuilder.Scheme; this.Host = uriBuilder.Host; this.Port = uriBuilder.Port; path = uriBuilder.Path; query = uriBuilder.Query; } else { var queryIndex = url.IndexOf('?'); if (queryIndex >= 0) { path = url.Substring(0, queryIndex); query = url.Substring(queryIndex + 1); } else { path = url; } } this.Paths.AddRange(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)); if (query != null) { var queryKeyValuePairs = HttpUtility.ParseQueryString(query); foreach (var queryKey in queryKeyValuePairs.AllKeys) { this.QueryPairs[queryKey] = queryKeyValuePairs[queryKey]; } } } public UrlBuilder AddPath(string value) { this.Paths.Add(value); return this; } public UrlBuilder SetQuery(string key, string value) { this.QueryPairs[key] = value; return this; } public UrlBuilder RemoveQuery(string key) { this.QueryPairs.Remove(key); return this; } public UrlBuilder AlterQuery(string key, Func<string, string> alterMethod, bool removeOnNull = false) { string value; this.QueryPairs.TryGetValue(key, out value); value = alterMethod(value); if (removeOnNull && value == null) { return this.RemoveQuery(key); } else { return this.SetQuery(key, value); } } public override string ToString() { var path = !string.IsNullOrWhiteSpace(this.Host) ? string.Join("/", this.Host, string.Join("/", this.Paths)) : string.Join("/", this.Paths); var query = string.Join("&", this.QueryPairs.Select(x => string.Concat(x.Key, "=", HttpUtility.UrlEncode(x.Value)))); return string.Concat( !string.IsNullOrWhiteSpace(this.Scheme) ? string.Concat(this.Scheme, "://") : null, path, !string.IsNullOrWhiteSpace(query) ? string.Concat("?", query) : null); } } }
I went with the solution proposed by DSO (answered on Aug 2 '11 at 7:29), his solution does not require using HttpUtility. However, as per an article posted in Dotnetpearls, using a Dictionary is faster (in performance) than using NameValueCollection. Here is DSO's solution modified to use Dictionary in place of NameValueCollection. public static Dictionary<string, string> QueryParametersDictionary() { var dictionary = new Dictionary<string, string>(); dictionary.Add("name", "John Doe"); dictionary.Add("address.city", "Seattle"); dictionary.Add("address.state_code", "WA"); dictionary.Add("api_key", "5352345263456345635"); return dictionary; } public static string ToQueryString(Dictionary<string, string> nvc) { StringBuilder sb = new StringBuilder(); bool first = true; foreach (KeyValuePair<string, string> pair in nvc) { if (!first) { sb.Append("&"); } sb.AppendFormat("{0}={1}", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value)); first = false; } return sb.ToString(); }
I wrote a helper for my razor project using some of the hints from other answers. The ParseQueryString business is necessary because we are not allowed to tamper with the QueryString object of the current request. #helper GetQueryStringWithValue(string key, string value) { var queryString = System.Web.HttpUtility.ParseQueryString(HttpContext.Current.Request.QueryString.ToString()); queryString[key] = value; #Html.Raw(queryString.ToString()) } I use it like this: location.search = '?#Helpers.GetQueryStringWithValue("var-name", "var-value")'; If you want it to take more than one value, just change the parameters to a Dictionary and add the pairs to the query string.
Refactoring method
I've following method in the code.I could have some more if conditions in the future. So just wondering how to re-factor this code from a maintenance point of view. string MyMethod(string filter) { StringBuilder sbFilter = new StringBuilder(filter); if (filter.Contains("A")) { sbFilter.Append("X"); } if (filter.Contains("B")) { sbFilter.Append("Y"); } if (filter.Contains("C")) { sbFilter.Append("Z"); } return sbFilter.ToString(); } Thanks.
You can put your from/to combinations into an array, which can easily be modified. With linq you can select the values: String FilterMethod(String filter) { var fromTo = new[] {"A,X","B,Y","C,Z"}; return String.Concat( from pair in fromTo let split= pair.Split(',') where split.Length == 2 && filter.Contains(split[0]) select split[1] ); } You can even make a one-liner: String FilterMethod2(String filter) { return String.Concat( from pair in new[] { "A,X", "B,Y", "C,Z" } let split = pair.Split(',') where split.Length == 2 && filter.Contains(split[0]) select split[1] ); }
You could simply use a list of tuples: KeyValuePair<string, string>[] mappings = new [] { new KeyValuePair<string, string>("A", "X"), new KeyValuePair<string, string>("B", "Y"), new KeyValuePair<string, string>("C", "Z"), }; string MyMethod(string filter) { var sbFilter = new StringBuilder(filter); foreach (var m in mappings) { if (filter.Contains(m.Key)) sbFilter.Append(m.Value); } return sbFilter.ToString(); } Which can be written even more concisely using LINQ: string MyMethod(string filter) { return filter + string.Concat(mappings .Where(m => filter.Contains(m.Key)) .Select(m => m.Value)); }
As # Jakub Dąbek wrote in his comment, this question is more suitable for code review forum, but I think that using a SortedDictionary will make your code more maintainable and clear. that way you will never have the need to change MyMethod function. based on your example, i would do something like that (please see my comments inside the code): private SortedDictionary<string, string> _Dic; public SortedDictionary<string, string> Dic { get { if (_Dic == null) { _Dic = new SortedDictionary<string, string>(); _Dic.Add("A", "X"); _Dic.Add("B", "Y"); _Dic.Add("C", "Z"); // add more key-pair values in the future return _Dic; } else { return _Dic; } } set { // important: here you can get your valus from external source! _Dic = value; } } string MyMethod(string filter) { StringBuilder sbFilter = new StringBuilder(filter); foreach (KeyValuePair<string, string> itm in Dic) { if (filter.Contains(itm.Key)) { sbFilter.Append(Dic[itm.Key]); } } return sbFilter.ToString(); }
Is there a Pattern for dealing with mainframe data?
NOTE: Clarified some of my question at the bottom. I am wondering if there might be a (sane) pattern to deal with request/response from older mainframe systems? In the examples below, IQ is the request and RSIQ is the response. In the first example, I am requesting a list of all account codes and in the second request I am asking for the Closed Date for each account code. Since these are only linked by the ordinal position it is easy enough to pull the data into a structured data class. Each response in this case represents multiple records. In the 2nd example I am requesting several bits of information for a single record. In this case, each response represents a single record and a smattering of data points. This is the message a client sends to the server to request specific information from the database. The inquiry message has this general format: IQ~<msg id>~A<unit#>~B<device type>~D<acct#>~F<password>~G<file>~H<hierarchicrecordpath>~J<field> **One field from many records**: Beginning with first share (ordinal zero) on Account 101 return all the Share ID fields in first message then get all Close Dates in second message. IDs and Close Dates correspond positionally within the two responses. IQ~1~A0~BVENDOR~D101~F7777~HSHARE=0~JID=ALL RSIQ~1~K0~JID=0000~JID=0003~JID=0004~JID=0005~JID=0025~JID=0050 IQ~1~A0~BVENDOR~D101~F7777~HSHARE=0~JCLOSEDATE=ALL RSIQ~1~K0~JCLOSEDATE=00000000~JCLOSEDATE=20030601~JCLOSEDATE=00000000~JCLOSEDATE=00000000~JCLOSEDATE=00000000~JCLOSEDATE=00000000 **Many fields from one record**: Using the previous requests get additional information from open shares (two examples). IQ~1~A0~BVENDOR~D101~F7777~HSHARE#0005~JCLOSEDATE~JSHARECODE~JDIVTYPE~JBALANCE~JAVAILABLEBALANCE RSIQ~1~K0~JCLOSEDATE=00000000~JSHARECODE=0~JDIVTYPE=2~JBALANCE=234567~JAVAILABLEBALANCE=234567 IQ~1~A0~BVENDOR~D101~F7777~HSHARE#0025~JCLOSEDATE~JSHARECODE~JDIVTYPE~JBALANCE~JAVAILABLEBALANCE RSIQ~1~K0~JCLOSEDATE=00000000~JSHARECODE=1~JDIVTYPE=5~JBALANCE=654321~JAVAILABLEBALANCE=654321 BACKGROUND: I am already using the Unit of Work/Repository pattern in my applications. Each application is dealing with multiple data stores (SQL DBs, Files, Web Services, Sockets, etc). The idea being that each Repository exposes a (part of the full) data model. My initial thinking is to create the specific calls I need in the Repository, like GetAccounts(acctId) and have the method send the correct requests and then build up the object graph from all the reponses, finally returning the object graph. I'm now looking for a design pattern to handle the internals of each of these methods without doing a ton of string.Replace() statements, or StringBuilder calls. Since the max size of any request is 8000 characters, you can see where the ~J fields can get quite complex. (And I am still looking for all the possible codes that can go in the ~J fields.) Smallish example: public List<SymitarAccount> GetAccounts(string accountId) { var retAccounts = new List<SymitarAccount>(); // Is there a pattern to do this repetitve but ever changing task? // // Example: Mock response then handle... // // NOTE: There will be many request/response calls here, not just one! // var rsp = #"RSIQ~1~K0~JCLOSEDATE=00000000~JSHARECODE=1~JDIVTYPE=5~JBALANCE=654321~JAVAILABLEBALANCE=654321"; var response = rsp.Split(new[] {'~'}); foreach (var q in response) { if (q.StartsWith("J") && q.Contains("=")) { // get Key Value Pair // // map KVP to SymitarAccount data point (big ugly switch(){}??) // sa.Id = // KVP for ID // sa.Balanace = // KVP for BALANCE // } retAccounts.Add(sa); } return retAccounts; } Any thoughts or ideas? NOTE: I am using C# (latest). ADDITION #1: public List<SymitarAccount> GetAccounts(string accountId) { var retAccounts = new List<SymitarAccount>(); // Get all account IDs... var response = UnitOfWork.SendMessage("IQ~1~A0~BVENDOR~D101~F7777~HSHARE=0~JID=ALL"); ParseResponse(response, ref retAccounts); // Get all account close dates (00000000 means it is open)... response = UnitOfWork.SendMessage("IQ~1~A0~BVENDOR~D101~F7777~HSHARE=0~JCLOSEDATE=ALL"); ParseResponse(response, ref retAccounts); // Get extra info for all OPEN accounts... foreach (var account in retAccounts.Where(a => !a.IsClosed)) { var request = "IQ~1~A0~BVENDOR~D101~F7777~HSHARE#[acct]~JCLOSEDATE~JSHARECODE~JDIVTYPE~JBALANCE~JAVAILABLEBALANCE"; request = request.Replace("[acct]", account.Id.ToString("0000")); response = UnitOfWork.SendMessage(request); ParseResponse(response, ref retAccounts, account.Id); } return retAccounts; } private void ParseResponse(string response, ref List<SymitarAccount> accountList, int? id = null) { var list = response.Split(new[] {'~'}); var index = 0; var chain = new ChainInquiryAccountInfo(); var parser = chain.Parser; foreach (var q in list.Where(q => q.StartsWith("J"))) // && q.Contains("="))) { if (accountList.Count < index || accountList[index] == null) accountList.Add(new SymitarAccount {PositionalIndex = index}); var val = q.Split(new[] {'='}); if ((id.HasValue && accountList[index].Id == id.Value) || !id.HasValue) accountList[index] = parser.Parse(val, accountList[index]); index++; } }
You example is in fact deserialization, not from XML or JSON but from some custom text format. You can go with the direction of other serializers then, when you create classes and attribute their fields to help serializing/deserializing. This can be called Attributed Serializer Pattern I believe... Let's create some custom attribute to annotate serialized classes: [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] sealed class SomeDataFormatAttribute : Attribute { readonly string name; // This is a positional argument public SomeDataFormatAttribute(string positionalString) { this.name = positionalString; } public string Name { get { return name; } } } and then you can describe your data objects as: class SymitarAccount { [SomeDataFormat("CLOSEDATE")] public string CloseDate; [SomeDataFormat("SHARECODE")] public int ShareCode; } Now you need serializer/deserializer based on Reflection, that will match attributed fields with string. Here I use regular expressions (and no error checking for simplicity): public class SomeDataFormatDeserializer { public static T Deserlize<T>(string str) where T : new() { var result = new T(); var pattern = #"RSIQ~1~K0(?:~J(\w+=\d+))*"; var match = Regex.Match(str, pattern); // Get fields of type T var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (var field in fields) { // Get out custom attribute of this field (might return null) var attr = field.GetCustomAttribute(typeof(SomeDataFormatAttribute)) as SomeDataFormatAttribute; // Find regex capture that starts with attributed name (might return null) var capture = match.Groups[1].Captures .Cast<Capture>() .FirstOrDefault(c => c.Value.StartsWith(attr.Name)); if (capture != null) { var stringValue = capture.Value.Split('=').Last(); // Convert string to the proper type (like int) var value = Convert.ChangeType(stringValue, field.FieldType); field.SetValue(result, value); } } return result; } } And then you can use it as simple as: public static List<SymitarAccount> GetAccounts(string accountId) { var retAccounts = new List<SymitarAccount>(); var responses = new List<string>() { #"RSIQ~1~K0~JCLOSEDATE=00000000~JSHARECODE=1" }; foreach (var response in responses) { var account = SomeDataFormatDeserializer.Deserlize<SymitarAccount>(response); retAccounts.Add(account); } return retAccounts; } Note: SomeDataFormatDeserializer is written for clarity, not performance. For sure it can be optimized (like caching GetFields etc.)
MY SOLUTION: Attribute definition: [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] internal sealed class SymitarInquiryDataFormatAttribute : Attribute { private readonly string _name; // This is a positional argument public SymitarInquiryDataFormatAttribute(string positionalString) { this._name = positionalString; } public string Name { get { return _name; } } } Data class: [Serializable] public class SymitarAccount { public int PositionalIndex; public bool IsClosed{get { return CloseDate.HasValue; }} [SymitarInquiryDataFormatAttribute("ID")] public int Id; [SymitarInquiryDataFormatAttribute("CLOSEDATE")] public DateTime? CloseDate; [SymitarInquiryDataFormatAttribute("DIVTYPE")] public int DivType; [SymitarInquiryDataFormatAttribute("BALANCE")] public decimal Balance; [SymitarInquiryDataFormatAttribute("AVAILABLEBALANCE")] public decimal AvailableBalance; } Extensions: public static class ExtensionSymitar { public static List<string> ValueList(this string source, string fieldType) { var list = source.Split('~').ToList(); return list.Where(a => a.StartsWith(fieldType)).ToList(); } public static string KeyValuePairs(this string source, string fieldType) { return source.ValueList(fieldType).Aggregate(string.Empty, (current, j) => string.Format("{0}~{1}", current, j)); } public static bool IsMultiRecord(this string source, string fieldType) { return source.ValueList(fieldType) .Select(q => new Regex(Regex.Escape(q.Split('=').First())).Matches(source).Count > 1).First(); } public static int ParseInt(this string val, string keyName) { int newValue; if (!int.TryParse(val, out newValue)) throw new Exception("Could not parse " + keyName + " as an integer!"); return newValue; } public static decimal ParseMoney(this string val, string keyName) { decimal newValue; if (!decimal.TryParse(val, out newValue)) throw new Exception("Could not parse " + keyName + " as a money amount!"); return newValue; } public static DateTime? ParseDate(this string val, string keyName) { if (val.Equals("00000000")) return null; var year = val.Substring(0, 4).ToInt(); var mon = val.Substring(4, 2).ToInt(); var day = val.Substring(6, 2).ToInt(); if (year <= 1800 || year >= 2200 || mon < 1 || mon > 12 || day < 1 || day > 31) throw new Exception("Could not parse " + keyName + " as a date!"); return new DateTime(year, mon, day); } } Deserializer: public class SymitarInquiryDeserializer { /// <summary> /// Deserializes a string of J field key value pairs /// </summary> /// <param name="str">The request or response string</param> /// <param name="source">Optional: Use this if you are adding data to the source object</param> /// <param name="fieldName">Optional: Use this if you are only populating a single property and know what it is</param> /// <typeparam name="T">The target class type to populate</typeparam> /// <returns>New T Object or optional Source Object</returns> public static T DeserializeFieldJ<T>(string str, T source = null, string fieldName = null) where T : class, new() { var result = source ?? new T(); const string pattern = #"(?:~J(\w+=\d+))*"; var match = Regex.Match(str, pattern); // Get fields of type T var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance).ToList(); if (fieldName != null && fieldName.StartsWith("J")) fieldName = fieldName.Replace("J", ""); if (!fieldName.IsNullOrEmpty()) { var field = fields.FirstOrDefault(a => a.Name.Equals(fieldName, StringComparison.CurrentCultureIgnoreCase)); var stringValue = GetValue(field, match); if (!stringValue.IsNullOrEmpty()) SetProperty(field, stringValue, result); } else { foreach (var field in fields) { var stringValue = GetValue(field, match); if(!stringValue.IsNullOrEmpty()) SetProperty(field, stringValue, result); } } return result; } private static string GetValue(FieldInfo field, Match match) { // Get out custom attribute of this field (might return null) var attr = field.GetCustomAttribute(typeof(SymitarInquiryDataFormatAttribute)) as SymitarInquiryDataFormatAttribute; if (attr == null) return null; // Find regex capture that starts with attributed name (might return null) var capture = match.Groups[1] .Captures .Cast<Capture>() .FirstOrDefault(c => c.Value.StartsWith(attr.Name, StringComparison.CurrentCultureIgnoreCase)); return capture == null ? null : capture.Value.Split('=').Last(); } private static void SetProperty<T>(FieldInfo field, string stringValue, T result) { // Convert string to the proper type (like int) if (field.FieldType.FullName.Contains("Int32")) field.SetValue(result, stringValue.ParseInt(field.Name)); else if (field.FieldType.FullName.Contains("Decimal")) field.SetValue(result, stringValue.ParseMoney(field.Name)); else if (field.FieldType.FullName.Contains("DateTime")) field.SetValue(result, stringValue.ParseDate(field.Name)); else { var value = Convert.ChangeType(stringValue, field.FieldType); field.SetValue(result, value); } } } Finally, in my repository: public List<SymitarAccount> GetAccounts(string accountId) { var accountList = new List<SymitarAccount>(); // build request, get response, parse it... var request = "IQ~1~A20424~BAUTOPAY~D101~F7777~HSHARE=0~JID=ALL"; var response = UnitOfWork.SendMessage(request); ParseResponse(response, ref accountList); foreach (var account in accountList.Where(a => a.IsClosed == false)) { request = "IQ~1~A20424~BAUTOPAY~D101~F7777~HSHARE#" + account.Id.ToString("0000") + "~JCLOSEDATE~JSHARECODE~JDIVTYPE~JBALANCE~JAVAILABLEBALANCE"; response = UnitOfWork.SendMessage(request); ParseResponse(response, ref accountList, account.Id); } return accountList; } private void ParseResponse(string response, ref List<SymitarAccount> accountList, int? id = null) { var index = 0; var list = response.ValueList(fieldType: "J"); var jString = response.KeyValuePairs(fieldType: "J"); var isMultiRecord = response.IsMultiRecord(fieldType: "J"); SymitarAccount account; if (isMultiRecord && !id.HasValue) foreach (var q in list.Where(a => a.StartsWith("J"))) { // Add object if we don't yet have it in the collection... if (accountList.Count <= index) accountList.Add(new SymitarAccount { PositionalIndex = index }); account = accountList.FirstOrDefault(a => a.PositionalIndex == index); SymitarInquiryDeserializer.DeserializeFieldJ("~" + q, account, q.Split('=').First()); index++; } else if(id.HasValue) { account = accountList.FirstOrDefault(a => a.Id == id.Value); SymitarInquiryDeserializer.DeserializeFieldJ(jString, account); } } The difference between the 2 calls to ParseResponse is, in the first case, I am asking for multiple records to be returned (only 1 data property though!) while in the second case I am requesting extra data properties for a single record be sent back.
Passing key/value anonymous object as parameter
In mvc i can use construction like this #Html.TextAreaFor(model => model.iEventSummary, new { #class = "test" }) I'm trying to reproduce this new { #class = "test" } as parameter but unsuccessfully testFunction( new {key1="value1", key2="value2", key3="" }) public static string testFunction(dynamic dict) { string ret = string.Empty; IDictionary<string, string> dictionary = dict; foreach (var item in dictionary) { ret += item.Key + item.Value; } return ret; } How does a method variable must be declared? If I want pass new {key1="value1", key2="value2", key3="" } as parameter.
You can convert anonymous object to IDictionary using RouteValueDictionary. Change your function to: public static string TestFunction(object obj) { var dict = new RouteValueDictionary(obj); var ret = ""; foreach (var item in dict) { ret += item.Key + item.Value.ToString(); } return ret; } And you can use it: TestFunction(new { key1="value1", key2="value2", key3="" });
public static string TestFunction(object obj) { //To dictionary //var dict = obj.GetType().GetProperties() // .ToDictionary(p=>p.Name,p=>p.GetValue(obj,null)); //Directly ToString string result = String.Join(",", obj.GetType().GetProperties() .Select(p=>p.Name + ":" + p.GetValue(obj,null))); return result; }
Create instance of generic type whose constructor requires a parameter?
If BaseFruit has a constructor that accepts an int weight, can I instantiate a piece of fruit in a generic method like this? public void AddFruit<T>()where T: BaseFruit{ BaseFruit fruit = new T(weight); /*new Apple(150);*/ fruit.Enlist(fruitManager); } An example is added behind comments. It seems I can only do this if I give BaseFruit a parameterless constructor and then fill in everything through member variables. In my real code (not about fruit) this is rather impractical. -Update- So it seems it can't be solved by constraints in any way then. From the answers there are three candidate solutions: Factory Pattern Reflection Activator I tend to think reflection is the least clean one, but I can't decide between the other two.
Additionally a simpler example: return (T)Activator.CreateInstance(typeof(T), new object[] { weight }); Note that using the new() constraint on T is only to make the compiler check for a public parameterless constructor at compile time, the actual code used to create the type is the Activator class. You will need to ensure yourself regarding the specific constructor existing, and this kind of requirement may be a code smell (or rather something you should just try to avoid in the current version on c#).
You can't use any parameterised constructor. You can use a parameterless constructor if you have a "where T : new()" constraint. It's a pain, but such is life :( This is one of the things I'd like to address with "static interfaces". You'd then be able to constrain T to include static methods, operators and constructors, and then call them.
Yes; change your where to be: where T:BaseFruit, new() However, this only works with parameterless constructors. You'll have to have some other means of setting your property (setting the property itself or something similar).
Most simple solution Activator.CreateInstance<T>()
As Jon pointed out this is life for constraining a non-parameterless constructor. However a different solution is to use a factory pattern. This is easily constrainable interface IFruitFactory<T> where T : BaseFruit { T Create(int weight); } public void AddFruit<T>( IFruitFactory<T> factory ) where T: BaseFruit { BaseFruit fruit = factory.Create(weight); /*new Apple(150);*/ fruit.Enlist(fruitManager); } Yet another option is to use a functional approach. Pass in a factory method. public void AddFruit<T>(Func<int,T> factoryDel) where T : BaseFruit { BaseFruit fruit = factoryDel(weight); /* new Apple(150); */ fruit.Enlist(fruitManager); }
You can do by using reflection: public void AddFruit<T>()where T: BaseFruit { ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) }); if (constructor == null) { throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor"); } BaseFruit fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit; fruit.Enlist(fruitManager); } EDIT: Added constructor == null check. EDIT: A faster variant using a cache: public void AddFruit<T>()where T: BaseFruit { var constructor = FruitCompany<T>.constructor; if (constructor == null) { throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor"); } var fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit; fruit.Enlist(fruitManager); } private static class FruitCompany<T> { public static readonly ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) }); }
As an addition to user1471935's suggestion: To instantiate a generic class by using a constructor with one or more parameters, you can now use the Activator class. T instance = Activator.CreateInstance(typeof(T), new object[] {...}) The list of objects are the parameters you want to supply. According to Microsoft: CreateInstance [...] creates an instance of the specified type using the constructor that best matches the specified parameters. There's also a generic version of CreateInstance (CreateInstance<T>()) but that one also does not allow you to supply constructor parameters.
I created this method: public static V ConvertParentObjToChildObj<T,V> (T obj) where V : new() { Type typeT = typeof(T); PropertyInfo[] propertiesT = typeT.GetProperties(); V newV = new V(); foreach (var propT in propertiesT) { var nomePropT = propT.Name; var valuePropT = propT.GetValue(obj, null); Type typeV = typeof(V); PropertyInfo[] propertiesV = typeV.GetProperties(); foreach (var propV in propertiesV) { var nomePropV = propV.Name; if(nomePropT == nomePropV) { propV.SetValue(newV, valuePropT); break; } } } return newV; } I use that in this way: public class A { public int PROP1 {get; set;} } public class B : A { public int PROP2 {get; set;} } Code: A instanceA = new A(); instanceA.PROP1 = 1; B instanceB = new B(); instanceB = ConvertParentObjToChildObj<A,B>(instanceA);
You can use the following command: T instance = (T)typeof(T).GetConstructor(new Type[0]).Invoke(new object[0]); Be sure to see the following reference.
Recently I came across a very similar problem. Just wanted to share our solution with you all. I wanted to I created an instance of a Car<CarA> from a json object using which had an enum: Dictionary<MyEnum, Type> mapper = new Dictionary<MyEnum, Type>(); mapper.Add(1, typeof(CarA)); mapper.Add(2, typeof(BarB)); public class Car<T> where T : class { public T Detail { get; set; } public Car(T data) { Detail = data; } } public class CarA { public int PropA { get; set; } public CarA(){} } public class CarB { public int PropB { get; set; } public CarB(){} } var jsonObj = {"Type":"1","PropA":"10"} MyEnum t = GetTypeOfCar(jsonObj); Type objectT = mapper[t] Type genericType = typeof(Car<>); Type carTypeWithGenerics = genericType.MakeGenericType(objectT); Activator.CreateInstance(carTypeWithGenerics , new Object[] { JsonConvert.DeserializeObject(jsonObj, objectT) });
If you are willing to use a c# precompiler, you could resolve this so that it does have compile time constraints: // Used attribute [AttributeUsage(AttributeTargets.Parameter)] class ResolvedAsAttribute : Attribute { public string Expression; public ResolvedAsAttribute(string expression) { this.Expression = expression; } } // Fruit manager source: class FruitManager { ... public void AddFruit<TFruit>([ResolvedAs("(int p) => new TFruit(p)")] Func<int,TFruit> ctor = null)where TFruit: BaseFruit{ BaseFruit fruit = ctor(weight); /*new Apple(150);*/ fruit.Enlist(fruitManager); } } // Fruit user source: #ResolveInclude ../Managers/FruitManager.cs ... fruitManager.AddFruit<Apple>(); ... Your precompiler would then turn the Fruit user source into: ... fruitManager.AddFruit<Apple>((int p) => new Apple(p)); ... Using Roslyn, your precompiler could look something like this (here is room for improvement): using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Symbols; using System.Threading; using System.Text.RegularExpressions; public class CsResolveIncludeAnalyser : CSharpSyntaxWalker { private List<(string key, MethodDeclarationSyntax node)> methodsToResolve = new List<(string key, MethodDeclarationSyntax node)>(); public List<(string key, MethodDeclarationSyntax node)> Analyse(string source) { var tree = CSharpSyntaxTree.ParseText(source); var syntaxRoot = tree.GetRoot(); Visit(tree.GetRoot()); return methodsToResolve; } public override void VisitMethodDeclaration(MethodDeclarationSyntax methodDeclaration) { base.VisitMethodDeclaration(methodDeclaration); if (methodDeclaration.ParameterList.Parameters.Count > 0) { foreach (var parm in methodDeclaration.ParameterList.Parameters) { var parmHasResolvedAs = parm.AttributeLists.Where((el) => el.Attributes.Where((attr) => attr.Name is IdentifierNameSyntax && ((IdentifierNameSyntax)attr.Name).Identifier.Text.Contains("ResolvedAs")).Any()).Any(); if (parmHasResolvedAs) { var name = methodDeclaration.Identifier.ValueText; methodsToResolve.Add((name, methodDeclaration)); return; } } } } } public class CsSwiftRewriter : CSharpSyntaxRewriter { private string currentFileName; private bool withWin32ErrorHandling; private Dictionary<string,MethodDeclarationSyntax> methodsToResolve = new Dictionary<string, MethodDeclarationSyntax>(); private Dictionary<string, MethodDeclarationSyntax> getMethodsToResolve(string source, string fileName) { Dictionary<string, MethodDeclarationSyntax> methodsToResolve = new Dictionary<string, MethodDeclarationSyntax>(); var path = Path.GetDirectoryName(fileName); var lines = source.Split(new[] { '\r', '\n' }); var resolveIncludes = (from el in lines where el.StartsWith("#ResolveInclude") select el.Substring("#ResolveInclude".Length).Trim()).ToList(); var analyser = new CsResolveIncludeAnalyser(); foreach (var resolveInclude in resolveIncludes) { var src = File.ReadAllText(path + "/" + resolveInclude); var list = analyser.Analyse(src); foreach (var el in list) { methodsToResolve.Add(el.key, el.node); } } return methodsToResolve; } public static string Convert(string source, string fileName) { return Convert(source, fileName, false); } public static string Convert(string source, string fileName, bool isWithWin32ErrorHandling) { var rewriter = new CsSwiftRewriter() { currentFileName = fileName, withWin32ErrorHandling = isWithWin32ErrorHandling }; rewriter.methodsToResolve = rewriter.getMethodsToResolve(source, fileName); var resolveIncludeRegex = new Regex(#"(\#ResolveInclude)\b"); source = resolveIncludeRegex.Replace(source, "//$1"); var tree = CSharpSyntaxTree.ParseText(source); var syntaxRoot = tree.GetRoot(); var result = rewriter.Visit(tree.GetRoot()); return "#line 1 \"" + Path.GetFileName(fileName) + "\"\r\n" + result.ToFullString(); } internal List<string> transformGenericArguments(List<string> arguments, GenericNameSyntax gName, TypeParameterListSyntax typeParameterList) { var res = new List<string>(); var typeParameters = typeParameterList.ChildNodes().ToList(); foreach (var argument in arguments) { var arg = argument; for (int i = 0; i < gName.TypeArgumentList.Arguments.Count; i++) { var key = typeParameters[i]; var replacement = gName.TypeArgumentList.Arguments[i].ToString(); var regex = new System.Text.RegularExpressions.Regex($#"\b{key}\b"); arg = regex.Replace(arg, replacement); } res.Add(arg); } return res; } const string prefix = ""; internal List<string> extractExtraArguments(MethodDeclarationSyntax methodDeclaration) { var res = new List<String>(); foreach (var parm in methodDeclaration.ParameterList.Parameters) { foreach (var attrList in parm.AttributeLists) { foreach (var attr in attrList.Attributes) { if (attr.Name is IdentifierNameSyntax && string.Compare(((IdentifierNameSyntax)attr.Name).Identifier.Text, "ResolvedAs") == 0) { var programmCode = attr.ArgumentList.Arguments.First().ToString().Trim(); var trimmedProgrammCode = (programmCode.Length >= 2 && programmCode[0] == '"' && programmCode[programmCode.Length - 1] == '"') ? programmCode.Substring(1, programmCode.Length - 2) : programmCode; res.Add(prefix + parm.Identifier.Text + ":" + trimmedProgrammCode); } } } } return res; } internal List<string> extractExtraArguments(MethodDeclarationSyntax methodDeclaration, SimpleNameSyntax name) { var arguments = extractExtraArguments(methodDeclaration); if (name != null && name is GenericNameSyntax) { var gName = name as GenericNameSyntax; return transformGenericArguments(arguments, gName, methodDeclaration.TypeParameterList); } return arguments; } public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax c_expressionStatement) { InvocationExpressionSyntax expressionStatement = (InvocationExpressionSyntax) base.VisitInvocationExpression(c_expressionStatement); List<string> addedArguments = null; switch (expressionStatement.Expression) { case MemberAccessExpressionSyntax exp: if (methodsToResolve.ContainsKey(exp.Name?.Identifier.ValueText)) { addedArguments = extractExtraArguments(methodsToResolve[exp.Name.Identifier.ValueText], exp.Name); } break; case GenericNameSyntax gName: if (methodsToResolve.ContainsKey(gName.Identifier.ValueText)) { addedArguments = extractExtraArguments(methodsToResolve[gName.Identifier.ValueText], gName); } break; default: var name = (from el in expressionStatement.ChildNodes() where el is GenericNameSyntax select (el as GenericNameSyntax)).FirstOrDefault(); if (name != default(GenericNameSyntax)) { if (methodsToResolve.ContainsKey(name.Identifier.ValueText)) { addedArguments = extractExtraArguments(methodsToResolve[name.Identifier.ValueText], name); } } break; } if (addedArguments?.Count > 0) { var addedArgumentsString = string.Join(",", addedArguments); var args = expressionStatement.ArgumentList.ToFullString(); var paras = $"({(expressionStatement.ArgumentList.Arguments.Count > 0 ? string.Join(",", args.Substring(1,args.Length - 2), addedArgumentsString) : addedArgumentsString)})" ; var argList = SyntaxFactory.ParseArgumentList(paras); return expressionStatement.WithArgumentList(argList); } return expressionStatement; } } The Precompiler could be called using a T4 script, optionally regenerating the source at compile time.
It is still possible, with high performance, by doing the following: // public List<R> GetAllItems<R>() where R : IBaseRO, new() { var list = new List<R>(); using ( var wl = new ReaderLock<T>( this ) ) { foreach ( var bo in this.items ) { T t = bo.Value.Data as T; R r = new R(); r.Initialize( t ); list.Add( r ); } } return list; } and // ///<summary>Base class for read-only objects</summary> public partial interface IBaseRO { void Initialize( IDTO dto ); void Initialize( object value ); } The relevant classes then have to derive from this interface and initialize accordingly. Please note, that in my case, this code is part of a surrounding class, which already has <T> as generic parameter. R, in my case, also is a read-only class. IMO, the public availability of Initialize() functions has no negative effect on the immutability. The user of this class could put another object in, but this would not modify the underlying collection.