190

I would like to parse a string such as p1=6&p2=7&p3=8 into a NameValueCollection.

What is the most elegant way of doing this when you don't have access to the Page.Request object?

SharpC
  • 6,974
  • 4
  • 45
  • 40
Nathan Smith
  • 36,807
  • 6
  • 28
  • 25

19 Answers19

370

There's a built-in .NET utility for this: HttpUtility.ParseQueryString

// C#
NameValueCollection qscoll = HttpUtility.ParseQueryString(querystring);
' VB.NET
Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)

You may need to replace querystring with new Uri(fullUrl).Query.

Carl Onager
  • 4,112
  • 2
  • 38
  • 66
Guy Starbuck
  • 21,603
  • 7
  • 53
  • 64
  • 26
    Omar, it didn't work for me on ASP.NET 4, it returned a key of "http://stackoverflow.com?para" instead of "para". So I'm using HttpUtility.ParseQueryString(new Uri(fullUrl).Query) which correctly works for me. – Michael Apr 05 '11 at 22:01
  • 2
    qscoll["p1"] , qscoll["p2"] and qscoll["p3"] – SMUsamaShah Sep 01 '11 at 20:28
  • 5
    ParseQueryString is really poor idea to use in desktop application, because it isn't included in Client Profile; why to install 100 M of additional libraries on client computer to just use one simple method? However, it looks like Microsoft doesn't have any better idea. The only way is to implement own method or use open source implementation. – Vitalii Jun 01 '12 at 12:05
  • 2
    VASoftOnline: In that case you can use the Mono implementation of ParseQueryString: https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web/HttpUtility.cs the license for that is MIT X11:https://github.com/mono/mono/blob/master/LICENSE – sw. Jan 04 '13 at 17:09
  • @Michael +1. `HttpUtility.ParseQueryString(new Uri(fullUrl).Query)` is a very important information. May be it should not be in the comment only. – fiberOptics Aug 27 '14 at 03:42
  • 4
    `HttpUtility.ParseQueryString` would be my recommendation except that in the case of `HttpUtility.ParseQueryString("&X=1&X=2&X=3")` the result is `....X=1,2,3...` Having multiple params of the same name is uncommon but needed to support controller parameters such as int[], IEnumerable etc (such params might be used to support multiple checkboxes) see "Multiple occurrences of the same query string variable are consolidated in one entry" as per [MS site](https://msdn.microsoft.com/en-us/library/ms150046(v=vs.110).aspx). A handcrafted version of the method might be your only option – dunxz Jun 15 '16 at 17:17
  • 1
    @Vitaliy While I agree it's stupid to need the whole System.Web dll to do this, there is quite a bit more that goes on in that method than simply parsing a url, including lots of calls to code that exists only in System.Web. You could likely get the source and build a custom implementation but it's not trivial. – Trevor Hart Dec 11 '17 at 19:28
  • If the value of a parameter has '&', then this method does not work properly. for example "?x1=123&X2=23&test" then it'll extract 3 parameters! – SeeSharp Oct 05 '18 at 14:27
  • 2018 Azure Comment - .Net CORE Full Framework - If you are avoiding System.Web references then the answers below from J Venema and A Sánchez are useful. – Sql Surfer Dec 07 '18 at 22:30
  • @dunxz Multiple parameters of the same name in a query string can be encoded the same way as the body of a `application/x-www-form-urlencoded` is encoded: the keys and values are encoded in key-value tuples separated by '&', with a '=' between the key and the value. Multiple values with the same key are part of the same collection. Not uncommon. – Suncat2000 Sep 09 '21 at 14:17
  • @SeeSharp It decodes 3 parameters as it should. `&` is a delimiter for a query string; an alternative character that can be used is `;`. So, yes, there _are_ 3 parameters there. If that is not what you want, the delimiters should be encoded so they are treated as part of a single value. – Suncat2000 Sep 09 '21 at 14:30
45

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 queryParameters = 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();

      queryParameters.Add(key, val);
   }
}
Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
  • 5
    you should check if parts.Length > 1, to be sure you can call parts[1] – Alexandru Pupsa Sep 29 '15 at 09:28
  • 3
    What if there's no value? e.g. a query string can look like `?foo=1&bar`. `HttpUtility` would parse it as `{ key = null, value = "bar" }` – Thomas Levesque Sep 20 '16 at 09:34
  • This is great compared to HttpUtility.ParseQueryString because it does not decode the values (so base64 encoded values are preserved) – CRice Jul 07 '17 at 01:30
  • 1
    Actually, you still need to allow the value '=' for example &code=A0SYw34Hj/m++lH0s7r0l/yg6GWdymzSCbI2zOn3V4o= will have the last '=' character removed. I rewrote part of your code to get the first index of '=' and substring the key and value to fix this (instead of using split). – CRice Jul 07 '17 at 01:51
  • There are many more corner cases that are not covered here. – Bogdan Gavril MSFT Oct 02 '20 at 10:47
  • @ThomasLevesque OMG, you're right! ParseQueryString() parses "bar" as a value instead of a key. Bug! – Suncat2000 Sep 09 '21 at 15:21
  • @Scott Dorman your answer should add a query parameter with only one part as a key with no (null or empty string) value. And probably not trim because spaces may be significant. And use `UnescapeDataString()` for the key and value because the parts may be encoded in the URI string. But simple and useful method. Thanks! – Suncat2000 Sep 09 '21 at 15:40
32

A lot of the answers are providing custom examples because of the accepted answer's dependency on System.Web. From the Microsoft.AspNet.WebApi.Client NuGet package there is a UriExtensions.ParseQueryString, method that can also be used:

var uri = new Uri("https://stackoverflow.com/a/22167748?p1=6&p2=7&p3=8");
NameValueCollection query = uri.ParseQueryString();

So if you want to avoid the System.Web dependency and don't want to roll your own, this is a good option.

James Skimming
  • 4,991
  • 4
  • 26
  • 32
  • 3
    The same function exists as an extension in the System.Net.Http namespace (see my answer below), no need for another whole dependency... – Jerod Venema Oct 24 '15 at 18:52
  • 1
    @jvenema From where are you adding the System.Net.Http.Formatting dependency, I believe it is only provided by adding the Microsoft.AspNet.WebApi.Client NuGet package. – James Skimming Nov 11 '15 at 18:18
  • it's a bit crazy that one has to add a whole new nuget package as a dependency, especially one that has AspNet in its name, which smells as only intended to be deployed in the server (in my case I need this for a mobile/desktop Xamarin app...) – knocte Aug 10 '20 at 12:45
21

I wanted to remove the dependency on System.Web so that I could parse the query string of a ClickOnce deployment, while having the prerequisites limited to the "Client-only Framework Subset".

I liked rp's answer. I added some additional logic.

public static NameValueCollection ParseQueryString(string s)
    {
        NameValueCollection nvc = new NameValueCollection();

        // remove anything other than query string from url
        if(s.Contains("?"))
        {
            s = s.Substring(s.IndexOf('?') + 1);
        }

        foreach (string vp in Regex.Split(s, "&"))
        {
            string[] singlePair = Regex.Split(vp, "=");
            if (singlePair.Length == 2)
            {
                nvc.Add(singlePair[0], singlePair[1]);
            }
            else
            {
                // only one key with no value specified in query string
                nvc.Add(singlePair[0], string.Empty);
            }
        }

        return nvc;
    }
densom
  • 695
  • 7
  • 8
  • this is really helpful for windows phone, you just have to replace the "NameValueCollection" with a "SortedDictionnary" – Mike Bryant Nov 14 '13 at 13:15
  • 5
    Be careful when switching NameValueCollection for a Dictionary - they're not the same! Query strings support multiple keys with the same value, and so does the NameValueCollection. – Matt DeKrey May 08 '14 at 13:10
9

To do this without System.Web, without writing it yourself, and without additional NuGet packages:

  1. Add a reference to System.Net.Http.Formatting
  2. Add using System.Net.Http;
  3. Use this code:

    new Uri(uri).ParseQueryString()
    

https://msdn.microsoft.com/en-us/library/system.net.http.uriextensions(v=vs.118).aspx

SharpC
  • 6,974
  • 4
  • 45
  • 40
Jerod Venema
  • 44,124
  • 5
  • 66
  • 109
  • 3
    From where are you adding the System.Net.Http.Formatting dependency, I believe it is only provided by adding the Microsoft.AspNet.WebApi.Client NuGet package. – James Skimming Nov 11 '15 at 18:18
  • Huh, my bad. I guess it came with the MVC framework that's auto-installed, so I didn't have to add any add'l packages (Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 4\Assemblies\System.Net.Http.Formatting.dll) – Jerod Venema Jun 22 '16 at 13:58
  • System.Net.Formatting EXTENSION VS2019 is a separate list or tab from the from traditional framework references – Sql Surfer Dec 07 '18 at 22:19
7

I needed a function that is a little more versatile than what was provided already when working with OLSC queries.

  • Values may contain multiple equal signs
  • Decode encoded characters in both name and value
  • Capable of running on Client Framework
  • Capable of running on Mobile Framework.

Here is my solution:

Public Shared Function ParseQueryString(ByVal uri As Uri) As System.Collections.Specialized.NameValueCollection
    Dim result = New System.Collections.Specialized.NameValueCollection(4)
    Dim query = uri.Query
    If Not String.IsNullOrEmpty(query) Then
        Dim pairs = query.Substring(1).Split("&"c)
        For Each pair In pairs
            Dim parts = pair.Split({"="c}, 2)

            Dim name = System.Uri.UnescapeDataString(parts(0))
            Dim value = If(parts.Length = 1, String.Empty,
                System.Uri.UnescapeDataString(parts(1)))

            result.Add(name, value)
        Next
    End If
    Return result
End Function

It may not be a bad idea to tack <Extension()> on that too to add the capability to Uri itself.

Josh Brown
  • 935
  • 1
  • 11
  • 21
4

If you don't want the System.Web dependency, just paste this source code from HttpUtility class.

I just whipped this together from the source code of Mono. It contains the HttpUtility and all it's dependencies (like IHtmlString, Helpers, HttpEncoder, HttpQSCollection).

Then use HttpUtility.ParseQueryString.

https://gist.github.com/bjorn-ali-goransson/b04a7c44808bb2de8cca3fc9a3762f9c

4

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.

Make sure to add a reference (if you haven't already) to System.Net.Http in your project.

Note that you have to convert the response body to a valid Uri so that ParseQueryString (in System.Net.Http)works.

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();
Amadeus Sanchez
  • 2,375
  • 2
  • 25
  • 31
  • System.Net.Formatting EXTENSION VS2019 puts extension references separate from traditional framework references. – Sql Surfer Dec 07 '18 at 22:14
3

I just realized that Web API Client has a ParseQueryString extension method that works on a Uri and returns a HttpValueCollection:

var parameters = uri.ParseQueryString();
string foo = parameters["foo"];
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • [System.Web.HttpUtility.ParseQueryString](https://learn.microsoft.com/en-us/dotnet/api/system.web.httputility.parsequerystring) also returns a `HttpValueCollection` (that is derived from `NameValueCollection` internally). Both have the same problem: they don't parse bare keys correctly. – Suncat2000 Sep 09 '21 at 15:19
2
    private void button1_Click( object sender, EventArgs e )
    {
        string s = @"p1=6&p2=7&p3=8";
        NameValueCollection nvc = new NameValueCollection();

        foreach ( string vp in Regex.Split( s, "&" ) )
        {
            string[] singlePair = Regex.Split( vp, "=" );
            if ( singlePair.Length == 2 )
            {
                nvc.Add( singlePair[ 0 ], singlePair[ 1 ] );    
            }    
        }
    }
rp.
  • 17,483
  • 12
  • 63
  • 79
  • 5
    semicolon is also allowed as a parameter separator in http, better not to reinvent the wheel – Matthew Lock Oct 28 '09 at 07:14
  • When the wheel is broken, better to replace it. – Suncat2000 Sep 09 '21 at 15:13
  • A woefully naive response from me that makes me want to crawl in a hole! It's the perfect definition of: I used regex to solve the problem, now I have two problems. – rp. Sep 10 '21 at 17:34
1

Since everyone seems to be pasting his solution.. here's mine :-) I needed this from within a class library without System.Web to fetch id parameters from stored hyperlinks.

Thought I'd share because I find this solution faster and better looking.

public static class Statics
    public static Dictionary<string, string> QueryParse(string url)
    {
        Dictionary<string, string> qDict = new Dictionary<string, string>();
        foreach (string qPair in url.Substring(url.IndexOf('?') + 1).Split('&'))
        {
            string[] qVal = qPair.Split('=');
            qDict.Add(qVal[0], Uri.UnescapeDataString(qVal[1]));
        }
        return qDict;
    }

    public static string QueryGet(string url, string param)
    {
        var qDict = QueryParse(url);
        return qDict[param];
    }
}

Usage:

Statics.QueryGet(url, "id")
SharpC
  • 6,974
  • 4
  • 45
  • 40
Tiele Declercq
  • 2,070
  • 2
  • 28
  • 39
  • 1
    Just one problem with this method: a query string can have more than one value for a given parameter. In this case, your method will throw a duplicate key error. – Thomas Levesque Jul 23 '14 at 21:31
  • yes, at least use a NameValueCollection, querystrings with duplicate keys are perfectly legal. – Chad Grant Apr 08 '17 at 23:26
  • Also, your dictionary is case sensitive, so X=1 and x=1 would be different keys. Need new Dictionary(StringComparer.OrdinalIgnoreCase); – Chad Grant Apr 08 '17 at 23:41
1

Just access Request.QueryString. AllKeys mentioned as another answer just gets you an array of keys.

Mike Becatti
  • 2,052
  • 1
  • 16
  • 32
1

HttpUtility.ParseQueryString(Request.Url.Query) return is HttpValueCollection (internal class). It inherits from NameValueCollection.

    var qs = HttpUtility.ParseQueryString(Request.Url.Query);
    qs.Remove("foo"); 

    string url = "~/Default.aspx"; 
    if (qs.Count > 0)
       url = url + "?" + qs.ToString();

    Response.Redirect(url); 
Zsolt Botykai
  • 50,406
  • 14
  • 85
  • 110
alex1kirch
  • 45
  • 5
0

To get all Querystring values try this:

    Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)

Dim sb As New StringBuilder("<br />")
For Each s As String In qscoll.AllKeys

  Response.Write(s & " - " & qscoll(s) & "<br />")

Next s
0
        var q = Request.QueryString;
        NameValueCollection qscoll = HttpUtility.ParseQueryString(q.ToString());
Hamit YILDIRIM
  • 4,224
  • 1
  • 32
  • 35
0

Hit up Request.QueryString.Keys for a NameValueCollection of all query string parameters.

Mark Glorie
  • 3,733
  • 3
  • 28
  • 31
-1

I translate to C# version of josh-brown in VB

private System.Collections.Specialized.NameValueCollection ParseQueryString(Uri uri)
{
    var result = new System.Collections.Specialized.NameValueCollection(4);
    var query = uri.Query;
    if (!String.IsNullOrEmpty(query))
    {
        var pairs = query.Substring(1).Split("&".ToCharArray());
        foreach (var pair in pairs)
        {
            var parts = pair.Split("=".ToCharArray(), 2);
            var name = System.Uri.UnescapeDataString(parts[0]);
            var value = (parts.Length == 1) ? String.Empty : System.Uri.UnescapeDataString(parts[1]);
            result.Add(name, value);
        }
    }
    return result;
}
elgoya
  • 101
  • 1
  • 4
-1
let search = window.location.search;

console.log(search);

let qString = search.substring(1);

while(qString.indexOf("+") !== -1)

   qString  = qString.replace("+", "");

let qArray = qString.split("&");

let values = [];

for(let i = 0; i < qArray.length; i++){
   let pos = qArray[i].search("=");
   let keyVal = qArray[i].substring(0, pos);
   let dataVal = qArray[i].substring(pos + 1);
   dataVal = decodeURIComponent(dataVal);
   values[keyVal] = dataVal;
}
-2

This is my code, I think it's very useful:

public String GetQueryString(string ItemToRemoveOrInsert = null, string InsertValue = null )
{
    System.Collections.Specialized.NameValueCollection filtered = new System.Collections.Specialized.NameValueCollection(Request.QueryString);
    if (ItemToRemoveOrInsert != null)
    {
        filtered.Remove(ItemToRemoveOrInsert);
        if (!string.IsNullOrWhiteSpace(InsertValue))
        {
            filtered.Add(ItemToRemoveOrInsert, InsertValue);
        }
    }

    string StrQr = string.Join("&", filtered.AllKeys.Select(key => key + "=" + filtered[key]).ToArray());
    if (!string.IsNullOrWhiteSpace(StrQr)){
        StrQr="?" + StrQr;
    }

    return StrQr;
}
Adi Inbar
  • 12,097
  • 13
  • 56
  • 69
Farhawd
  • 35
  • 1
  • 5