Replace {x} tokens in strings - c#

We have a template URL like:
http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}
and I have values for these constant tokens. How can replace all these tokens in C#?

A simple approach is to use a foreach and a Dictionary with a String.Replace:
var values = new Dictionary<string, string> {
{ "{networkid}", "WHEEE!!" }
// etc.
};
var url = "http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";
foreach(var key in values.Keys){
url = url.Replace(key,values[key]);
}

There is no standard way to "replace with dictionary values" in .NET. While there are a number of template engines, it's not very hard to write a small solution for such an operation. Here is an example which runs in LINQPad and utilizes a Regular Expression with a Match Evaluator.
As the result is a URL,
it is the callers responsibility to make sure all the supplied values are correctly encoded. I recommend using Uri.EscapeDataString as appropriate .. but make sure to not double-encode, if it is processed elsewhere.
Additionally, the rules of what to do when no replacement is found should be tailored to need. If not-found replacements should be eliminated entirely along with the query string key, the following can expand the regular expression to #"\w+=({\w+})" to also capture the parameter key in this specific template situation.
string ReplaceUsingDictionary (string src, IDictionary<string, object> replacements) {
return Regex.Replace(src, #"{(\w+)}", (m) => {
object replacement;
var key = m.Groups[1].Value;
if (replacements.TryGetValue(key, out replacement)) {
return Convert.ToString(replacement);
} else {
return m.Groups[0].Value;
}
});
}
void Main()
{
var replacements = new Dictionary<string, object> {
{ "networkid", "WHEEE!!" }
// etc.
};
var src = "http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";
var res = ReplaceUsingDictionary(src, replacements);
// -> "http://api.example.com/sale?..&networkid=WHEEE!!&..&pageid={pageid}&..
res.Dump();
}
More advanced techniques, like reflection and transforms, are possible - but those should be left for the real template engines.

I am guessing you are trying to replace parameters in url with your values. This can be done using C# HttpUtility.ParseQueryString
Get the CurrentURL from
var _myUrl = System.Web.HttpUtility.ParseQueryString(Request.RawUrl);
Read Parameter from your Query string
string value1 = _myUrl ["networkid"];
Write a value into the QueryString object.
_myUrl ["networkid"] = "Your Value";
and then finally turn it back into URL
var _yourURIBuilder= new UriBuilder(_myUrl );
_myUrl = _yourURIBuilder.ToString();

You can use this alos using LinQ
Dictionary<string, string> myVal = new Dictionary<string, string>();
myVal.Add("networkid", "1");
myVal.Add("pageid", "2");
myVal.Add("master", "3");
myVal.Add("optinfo", "4");
myVal.Add("publisher", "5");
myVal.Add("userId", "6");
string url = #"http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";
myVal.Select(a => url = url.Replace(string.Concat("{", a.Key, "}"), a.Value)).ToList();
this line can do your required functionlity
myVal.Select(a => url = url.Replace(string.Concat("{", a.Key, "}"), a.Value)).ToList();

There is a Nuget called StringTokenFormatter that does this well
https://www.nuget.org/packages/StringTokenFormatter/

Regex.Replace makes a single pass over a template string, offering you an opportunity to replace matched expressions. Use it by creating an regular expression that matches any token. Then look up replacement values for the tokens in a dictionary.
static string ReplaceTokens(string template, Dictionary<string, string> replacements) =>
Regex.Replace(template, #"{(\w+)}",
match => replacements.TryGetValue(match.Groups[1].Value, out string replacement) ? replacement : match.Value);
The algorithm completes in time linear with the size of the template string and the replacement strings, so O(t + r). Beware of algorithms that make multiple passes. They run slowly in time O(t * r) and will give incorrect results if one of the replacement values contains a token for later replacement. This unit test shows the pitfall:
public void TestReplaceTokens() {
var replacements = new Dictionary<string, string> {
["Movement"] = "drive {DontReplace}",
["DontReplace"] = "Should not appear"
};
string withReplacements = ReplaceTokens("I always {Movement} in {Direction}.", replacements);
Assert.AreEqual("I always drive {DontReplace} in {Direction}.", withReplacements);
}

Related

replacing data in between specific characters

I have a string which has some keys between <<>>.
string s = "<p>Hi <<USER>>,<br/>How are you doing<br/>Regards,<br/><<SENDER>></p>";
I want to first fetch the key names USER and SENDER in a list which i did by:
var keys = new List<string>();
foreach (Match match in Regex.Matches(s, #"<<(.*?)>>"))
{
keys.Add(match.Groups[1].Value);
}
List<string> values= new List<string>(){"John","Team"};
After we get the keys,i want to replace these keys by another list(named values here) which has the values for these keys and want the result as:
string s = "<p>Hi John,<br/>How are you doing<br/>Regards,<br/>Team</p>";
The string s can be anything and the no of keys and their values could also vary but the keys will always be enclosed in between <<>>
As suggested by #AlexBell, you could simply use the String.Replace() method.
Further, it's more convenient to declare a collection of placeholder/value pairs, like so:
string s = "<p>Hi <<USER>>,<br/>How are you doing<br/>Regards,<br/><<SENDER>></p>";
Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("<<USER>>", "Jhon");
dictionary.Add("<<SENDER>>", "Team");
StringBuilder text = new StringBuilder(s);
foreach (var entry in dictionary)
{
text.Replace(entry.Key, entry.Value);
}
Console.WriteLine(text.ToString());
This function will perform the replacements that you ask for, using Regex.Replace:
public static string ParseTemplate(string template, string username, string senderName)
{
template = Regex.Replace(template, #"<<USER>>", username);
return Regex.Replace(template, #"<<SENDER>>", senderName);
}
Example:
string s = "<p>Hi <<USER>>,<br/>How are you doing<br/>Regards,<br/><<SENDER>></p>";
ParseTemplate(s, "John", "Team").Dump();
Output:
<p>Hi John,<br/>How are you doing<br/>Regards,<br/>Team</p>
You can call this in a loop over your dictionary or list of names.
Your business logic is a bit unclear, so based on just qualified guess, you can apply standard .NET/C# String.Replace Method (String, String)
(re:https://msdn.microsoft.com/en-us/library/fk49wtc1%28v=vs.110%29.aspx), for example:
string s = "<p>Hi <<USER>>,<br/>How are you doing<br/>Regards,<br/><<SENDER>></p>".Replace("<USER>", "John").Replace("<SENDER>", "Team");
Hope this may help.
//First we have lists of values (users) and senders
List<string> values= new List<string>(){"John","Team"};
List<string> senders = new List<string>(){"John","Team"};
//Then we can join that list using string.join
var allUsers = string.Join(",", values);
var allSender = string.Join(",", senders);
//Next we will be replacing it in our string
var namedString = Regex.Replace(string, #"<<USER>>", allUsers);
var output = Regex.Replace(namedString , #"<<SENDER>>", allSender);

C# RegEx a Dictionary to replace occurances of a string

I am trying to replace occurrences of a property name with a value in a Dictionary in C#.
I have the following Dictionary:
Dictionary<string, string> properties = new Dictionary<string, string>()
{
{ "property1", #"E:\" },
{ "property2", #"$(property1)\Temp"},
{ "property3", #"$(property2)\AnotherSubFolder"}
};
Where the key is the property name, and the value is just a string value. I basically want to iterate over the values until all set properties have been replaced. The syntax is similar to MSBuild property names.
This should eventually evaluate property 3 to E:\Temp\AnotherSubFolder.
It would help if the RegEx part of the functionality would work, which is where I am stuck on.
I had tried out editing my RegEx on REFiddle here.
The following regex pattern works here:
/\$\(([^)]+)\)/g
Given the text:
$(property2)\AnotherSubFolder
It highlights the $(property2).
However, putting this together in .NET fiddle, I don't get any matches with the following code:
var pattern = #"\$\(([^)]+)\)/g";
Console.WriteLine(Regex.Matches(#"$(property2)AnotherSubFolder", pattern).Count);
Which outputs 0.
I am not too sure why here. Why is my match returning zero results?
.NET should match globally by default.
I'm not aware of support for /g as that is a Perl-ism, so remove it, and the leading /, .NET is trying to match them literally.
Regular Expressions may be overkill here, and may even introduce issues if your properties or values contain special characters, or characters that will be evaluated as regular expressions themselves.
A simple replacement should work:
Dictionary<string, string> properties = new Dictionary<string, string>()
{
{ "property1", #"E:\" },
{ "property2", #"$(property1)\Temp"},
{ "property3", #"$(property2)\AnotherSubFolder"}
};
Dictionary<string, string> newproperties = new Dictionary<string, string>();
// Iterate key value pairs in properties dictionary, evaluate values
foreach ( KeyValuePair<string,string> kvp in properties ) {
string value = kvp.Value;
// Execute replacements on value until no replacements are found
// (Replacement of $(property2) will result in value containing $(property1), must be evaluated again)
bool complete = false;
while (!complete) {
complete = true;
// Look for each replacement token in dictionary value, execute replacement if found
foreach ( string key in properties.Keys ) {
string token = "$(" + key + ")";
if ( value.Contains( token ) ) {
value = value.Replace( "$(" + key + ")", properties[key] );
complete = false;
}
}
}
newproperties[kvp.Key] = value;
}
properties = newproperties;

How to do a multiple case insensitive replace using a StringBuilder

I have a (large) template and want to replace multiple values. The replacement needs to be done case insensitive. It must also be possible to have keys that does not exist in the template.
For example:
[TestMethod]
public void ReplaceMultipleWithIgnoreCaseText()
{
const string template = "My name is #Name# and I like to read about #SUBJECT# on #website#, tag #subject#";
const string expected = "My name is Alex and I like to read about C# on stackoverflow.com, tag C#";
var replaceParameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("#name#","Alex"),
new KeyValuePair<string, string>("#subject#","C#"),
new KeyValuePair<string, string>("#website#","stackoverflow.com"),
// Note: The next key does not exist in template
new KeyValuePair<string, string>("#country#","The Netherlands"),
};
var actual = ReplaceMultiple(template, replaceParameters);
Assert.AreEqual(expected, actual);
}
public string ReplaceMultiple(
string template,
IEnumerable<KeyValuePair<string, string>> replaceParameters)
{
throw new NotImplementedException(
"Implementation needed for many parameters and long text.");
}
Note that if I have a list of 30 parameters and a large template, I do not want 30 large strings in memory. Using a StringBuilder seems to be an option, but other solutions are also welcome.
Solution I tried but did not work
Solution found here (C# String replace with dictionary) throws an exception when a key is not in the colletion, but our users makes mistakes and in that case I want to just leave the wromg key in the text. Example:
static readonly Regex re = new Regex(#"\$(\w+)\$", RegexOptions.Compiled);
static void Main2()
{
// "Name" is accidentally typed by a user as "nam".
string input = #"Dear $nam$, as of $date$ your balance is $amount$";
var args = new Dictionary<string, string>(
StringComparer.OrdinalIgnoreCase) {
{"name", "Mr Smith"},
{"date", "05 Aug 2009"},
{"amount", "GBP200"}};
// Works, but not case insensitive and
// uses a lot of memory when using a large template
// ReplaceWithDictionary many args
string output1 = input;
foreach (var arg in args)
{
output1 = output1.Replace("$" + arg.Key +"$", arg.Value);
}
// Throws a KeyNotFoundException + Only works when data is tokenized
string output2 = re.Replace(input, match => args[match.Groups[1].Value]);
}
Using a StringBuilder seems to be an option, but other solutions are also welcome.
Since you want case insensitive, I'd suggest (non StringBuilder):
public static string ReplaceMultiple(
string template,
IEnumerable<KeyValuePair<string, string>> replaceParameters)
{
var result = template;
foreach(var replace in replaceParameters)
{
var templateSplit = Regex.Split(result,
replace.Key,
RegexOptions.IgnoreCase);
result = string.Join(replace.Value, templateSplit);
}
return result;
}
DotNetFiddle Example
This is based off of Marc's answer the only real change is the check during the replacement and the boundary regex rule:
static readonly Regex re = new Regex(#"\b(\w+)\b", RegexOptions.Compiled);
static void Main(string[] args)
{
string input = #"Dear Name, as of dAte your balance is amounT!";
var replacements = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{"name", "Mr Smith"},
{"date", "05 Aug 2009"},
{"amount", "GBP200"}
};
string output = re.Replace(input, match => replacements.ContainsKey(match.Groups[1].Value) ? replacements[match.Groups[1].Value] : match.Groups[1].Value);
}
And here is a 5000 iterations test benchmark, have not looked at memory or anything else.
Replacement function is the one you have checked as the accepted answer.
I think I might have something you could try. I used something similar to it for email templates
public string replace()
{
string appPath = Request.PhysicalApplicationPath;
StreamReader sr = new StreamReader(appPath + "EmailTemplates/NewMember.txt");
string template = sr.ReadToEnd();
template = template.Replace("<%Client_Name%>",
first_name.Text + " " + middle_initial.Text + " " + last_name.Text);
//Add Customer data
template = template.Replace("<%Client_First_Name%>", first_name.Text);
template = template.Replace("<%Client_MI%>", middle_initial.Text);
template = template.Replace("<%Client_Last_Name%>", last_name.Text);
template = template.Replace("<%Client_DOB%>", dob.Text);
return template;
}
Inside of your template you can have tags such as <% %> as place holders for the values you want
Hope this helps!
The answer of Marc Gravell: C# String replace with dictionary can be changed an little bit so it does not throws an exception when the match can not be found. In this case it simply does not replace the match.
In case the string to be replace is tokenized, this is the solution:
static readonly Regex RegExInstance = new Regex(#"\$(\w+)\$", RegexOptions.Compiled);
public string ReplaceWithRegEx(string template, Dictionary<string, string> parameters)
{
return RegExInstance.Replace(template, match => GetNewValue(parameters, match));
}
private string GetNewValue(Dictionary<string, string> parameters, Match match)
{
var oldValue = match.Groups[1].Value;
string newValue;
var found = parameters.TryGetValue(oldValue, out newValue);
if (found)
{
return newValue;
}
var originalValue = match.Groups[0].Value;
return originalValue;
}
I have tested the solution with a 100.000 bytes string, 7 keys and hundreds of replacements. It uses 7 times more memory then the lenght of the string. And it took only 0.002 seconds.

Alternative to if, else if

I have a lot of if, else if statements and I know there has to be a better way to do this but even after searching stackoverflow I'm unsure of how to do so in my particular case.
I am parsing text files (bills) and assigning the name of the service provider to a variable (txtvar.Provider) based on if certain strings appear on the bill.
This is a small sample of what I'm doing (don't laugh, I know it's messy). All in all, There are approximately 300 if, else if's.
if (txtvar.BillText.IndexOf("SWGAS.COM") > -1)
{
txtvar.Provider = "Southwest Gas";
}
else if (txtvar.BillText.IndexOf("georgiapower.com") > -1)
{
txtvar.Provider = "Georgia Power";
}
else if (txtvar.BillText.IndexOf("City of Austin") > -1)
{
txtvar.Provider = "City of Austin";
}
// And so forth for many different strings
I would like to use something like a switch statement to be more efficient and readable but I'm unsure of how I would compare the BillText. I'm looking for something like this but can't figure out how to make it work.
switch (txtvar.BillText)
{
case txtvar.BillText.IndexOf("Southwest Gas") > -1:
txtvar.Provider = "Southwest Gas";
break;
case txtvar.BillText.IndexOf("TexasGas.com") > -1:
txtvar.Provider = "Texas Gas";
break;
case txtvar.BillText.IndexOf("Southern") > -1:
txtvar.Provider = "Southern Power & Gas";
break;
}
I'm definitely open to ideas.
I would need the ability to determine the order in which the values were evaluated.
As you can imagine, when parsing for hundreds of slightly different layouts I occasionally run into the issue of not having a distinctly unique indicator as to what service provider the bill belongs to.
Why not use everything C# has to offer? The following use of anonymous types, collection initializers, implicitly typed variables, and lambda-syntax LINQ is compact, intuitive, and maintains your modified requirement that patterns be evaluated in order:
var providerMap = new[] {
new { Pattern = "SWGAS.COM" , Name = "Southwest Gas" },
new { Pattern = "georgiapower.com", Name = "Georgia Power" },
// More specific first
new { Pattern = "City of Austin" , Name = "City of Austin" },
// Then more general
new { Pattern = "Austin" , Name = "Austin Electric Company" }
// And for everything else:
new { Pattern = String.Empty , Name = "Unknown" }
};
txtVar.Provider = providerMap.First(p => txtVar.BillText.IndexOf(p.Pattern) > -1).Name;
More likely, the pairs of patterns would come from a configurable source, such as:
var providerMap =
System.IO.File.ReadLines(#"C:\some\folder\providers.psv")
.Select(line => line.Split('|'))
.Select(parts => new { Pattern = parts[0], Name = parts[1] }).ToList();
Finally, as #millimoose points out, anonymous types are less useful when passed between methods. In that case we can define a trival Provider class and use object initializers for nearly identical syntax:
class Provider {
public string Pattern { get; set; }
public string Name { get; set; }
}
var providerMap =
System.IO.File.ReadLines(#"C:\some\folder\providers.psv")
.Select(line => line.Split('|'))
.Select(parts => new Provider() { Pattern = parts[0], Name = parts[1] }).ToList();
Since you seem to need to search for the key before returning the value a Dictionary is the right way to go, but you will need to loop over it.
// dictionary to hold mappings
Dictionary<string, string> mapping = new Dictionary<string, string>();
// add your mappings here
// loop over the keys
foreach (KeyValuePair<string, string> item in mapping)
{
// return value if key found
if(txtvar.BillText.IndexOf(item.Key) > -1) {
return item.Value;
}
}
EDIT: If you wish to have control over the order in which elemnts are evaluated, use an OrderedDictionary and add the elements in the order in which you want them evaluated.
One more using LINQ and Dictionary
var mapping = new Dictionary<string, string>()
{
{ "SWGAS.COM", "Southwest Gas" },
{ "georgiapower.com", "Georgia Power" }
.
.
};
return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
.Select(pair => pair.Value)
.FirstOrDefault();
If we prefer empty string instead of null when no key matches we can use the ?? operator:
return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
.Select(pair => pair.Value)
.FirstOrDefault() ?? "";
If we should consider the dictionary contains similar strings we add an order by, alphabetically, shortest key will be first, this will pick 'SCE' before 'SCEC'
return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
.OrderBy(pair => pair.Key)
.Select(pair => pair.Value)
.FirstOrDefault() ?? "";
To avoid the blatant Schlemiel the Painter's approach that looping over all the keys would involve: let's use regular expressions!
// a dictionary that holds which bill text keyword maps to which provider
static Dictionary<string, string> BillTextToProvider = new Dictionary<string, string> {
{"SWGAS.COM", "Southwest Gas"},
{"georgiapower.com", "Georgia Power"}
// ...
};
// a regex that will match any of the keys of this dictionary
// i.e. any of the bill text keywords
static Regex BillTextRegex = new Regex(
string.Join("|", // to alternate between the keywords
from key in BillTextToProvider.Keys // grab the keywords
select Regex.Escape(key))); // escape any special characters in them
/// If any of the bill text keywords is found, return the corresponding provider.
/// Otherwise, return null.
string GetProvider(string billText)
{
var match = BillTextRegex.Match(billText);
if (match.Success)
// the Value of the match will be the found substring
return BillTextToProvider[match.Value];
else return null;
}
// Your original code now reduces to:
var provider = GetProvider(txtvar.BillText);
// the if is be unnecessary if txtvar.Provider should be null in case it can't be
// determined
if (provider != null)
txtvar.Provider = provider;
Making this case-insensitive is a trivial exercise for the reader.
All that said, this does not even pretend to impose an order on which keywords to look for first - it will find the match that's located earliest in the string. (And then the one that occurs first in the RE.) You do however mention that you're searching through largeish texts; if .NET's RE implementation is at all good this should perform considerably better than 200 naive string searches. (By only making one pass through the string, and maybe a little by merging common prefixes in the compiled RE.)
If ordering is important to you, you might want to consider looking for an implementation of a better string search algorithm than .NET uses. (Like a variant of Boyer-Moore.)
What you want is a Dictionary:
Dictionary<string, string> mapping = new Dictionary<string, string>();
mapping["SWGAS.COM"] = "Southwest Gas";
mapping["foo"] = "bar";
... as many as you need, maybe read from a file ...
Then just:
return mapping[inputString];
Done.
One way of doing it (other answers show very valid options):
void Main()
{
string input = "georgiapower.com";
string output = null;
// an array of string arrays...an array of Tuples would also work,
// or a List<T> with any two-member type, etc.
var search = new []{
new []{ "SWGAS.COM", "Southwest Gas"},
new []{ "georgiapower.com", "Georgia Power"},
new []{ "City of Austin", "City of Austin"}
};
for( int i = 0; i < search.Length; i++ ){
// more complex search logic could go here (e.g. a regex)
if( input.IndexOf( search[i][0] ) > -1 ){
output = search[i][1];
break;
}
}
// (optional) check that a valid result was found.
if( output == null ){
throw new InvalidOperationException( "A match was not found." );
}
// Assign the result, output it, etc.
Console.WriteLine( output );
}
The main thing to take out of this exercise is that creating a giant switch or if/else structure is not the best way to do it.
There are several approaches to do this, but for the reason of simplicity, conditional operator may be a choice:
Func<String, bool> contains=x => {
return txtvar.BillText.IndexOf(x)>-1;
};
txtvar.Provider=
contains("SWGAS.COM")?"Southwest Gas":
contains("georgiapower.com")?"Georgia Power":
contains("City of Austin")?"City of Austin":
// more statements go here
// if none of these matched, txtvar.Provider is assigned to itself
txtvar.Provider;
Note the result is according to the more preceded condition which is met, so if txtvar.BillText="City of Austin georgiapower.com"; then the result would be "Georgia Power".
you can use dictionary.
Dictionary<string, string> textValue = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> textKey in textValue)
{
if(txtvar.BillText.IndexOf(textKey.Key) > -1)
return textKey.Value;
}

Easiest way to parse "querystring" formatted data

With the following code:
string q = "userID=16555&gameID=60&score=4542.122&time=343114";
What would be the easiest way to parse the values, preferably without writing my own parser? I'm looking for something with the same functionality as Request.querystring["gameID"].
Pretty easy... Use the HttpUtility.ParseQueryString method.
Untested, but this should work:
var qs = "userID=16555&gameID=60&score=4542.122&time=343114";
var parsed = HttpUtility.ParseQueryString(qs);
var userId = parsed["userID"];
// ^^^^^^ Should be "16555". Note this will be a string of course.
You can do it with linq like this.
string query = "id=3123123&userId=44423&format=json";
Dictionary<string,string> dicQueryString =
query.Split('&')
.ToDictionary(c => c.Split('=')[0],
c => Uri.UnescapeDataString(c.Split('=')[1]));
string userId = dicQueryString["userID"];
Edit
If you can use HttpUtility.ParseQueryString then it will be a lot more straight forward and it wont be case-sensitive as in case of LinQ.
As has been mentioned in each of the previous answers, if you are in a context where you can add a dependency to the System.Web library, using HttpUtility.ParseQueryString makes sense. (For reference, the relevant source can be found in the Microsoft Reference Source). However, if this is not possible, I would like to propose the following modification to Adil's answer which accounts for many of the concerns addressed in the comments (such as case sensitivity and duplicate keys):
var q = "userID=16555&gameID=60&score=4542.122&time=343114";
var parsed = q.TrimStart('?')
.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries)
.Select(k => k.Split('='))
.Where(k => k.Length == 2)
.ToLookup(a => a[0], a => Uri.UnescapeDataString(a[1])
, StringComparer.OrdinalIgnoreCase);
var userId = parsed["userID"].FirstOrDefault();
var time = parsed["TIME"].Select(v => (int?)int.Parse(v)).FirstOrDefault();
If you want to avoid the dependency on System.Web that is required to use HttpUtility.ParseQueryString, you could use the Uri extension method ParseQueryString found in System.Net.Http.
Note that you have to convert the response body to a valid Uri so that ParseQueryString works.
Please also note in the MSDN document, this method is an extension method for the Uri class, so you need reference the assembly System.Net.Http.Formatting (in System.Net.Http.Formatting.dll). I tried installed it by the nuget package with the name "System.Net.Http.Formatting", and it works fine.
string body = "value1=randomvalue1&value2=randomValue2";
// "http://localhost/query?" is added to the string "body" in order to create a valid Uri.
string urlBody = "http://localhost/query?" + body;
NameValueCollection coll = new Uri(urlBody).ParseQueryString();
How is this
using System.Text.RegularExpressions;
// query example
// "name1=value1&name2=value2&name3=value3"
// "?name1=value1&name2=value2&name3=value3"
private Dictionary<string, string> ParseQuery(string query)
{
var dic = new Dictionary<string, string>();
var reg = new Regex("(?:[?&]|^)([^&]+)=([^&]*)");
var matches = reg.Matches(query);
foreach (Match match in matches) {
dic[match.Groups[1].Value] = Uri.UnescapeDataString(match.Groups[2].Value);
}
return dic;
}
System.Net.Http ParseQueryString extension method worked for me. I'm using OData query options and trying to parse out some custom parameters.
options.Request.RequestUri.ParseQueryString();
Seems to give me what I need.
HttpUtility.ParseQueryString will work as long as you are in a web app or don't mind including a dependency on System.Web. Another way to do this is:
// NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(queryString);
NameValueCollection nameValueCollection = new NameValueCollection();
string[] querySegments = queryString.Split('&');
foreach(string segment in querySegments)
{
string[] parts = segment.Split('=');
if (parts.Length > 0)
{
string key = parts[0].Trim(new char[] { '?', ' ' });
string val = parts[1].Trim();
nameValueCollection.Add(key, val);
}
}
For .NET Core there is Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery
var queryString = QueryHelpers.ParseQuery("?param1=value");
var queryParamValue = queryString["param1"];
Code snippet modified from trackjs.com:

Categories