简体   繁体   中英

ToString() of copied NameValueCollection doesn't output desired results

I have a NameValueCollection in a usercontrol that is initialized like so:

private NameValueCollection _nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());

When I call the ToString() on this it generates a proper querystring which I can use for an updated url.

However, when I copy the NameValueCollection via its constructor like so:

var nameValues = new NameValueCollection(_nameValues);

And then try to form an url:

var newUrl = String.Concat(_rootPath + "?" + nameValues.ToString());

It outputs an url like this:

" http://www.domain.com?System.Collections.Specialized.NameValueCollection "

How can I copy a NameValueCollection so that the ToString() method outputs desired results?

The problem is there are two actual types in your code. The fist one is System.Web.HttpValueCollection which has it's ToString method overriden to get the result you expect and the second one is System.Collection.Specialized.NameValueCollection which does not override ToString. What you can do, if you really need to use System.Collection.Specialized.NameValueCollection is to create an extension method.

 public static string ToQueryString(this NameValueCollection collection)
 {
        var array = (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("&", array);
    }

and use it:

var newUrl = String.Concat(_rootPath,nameValues.ToQueryString());

It is not NameValueCollection that provides the string formatting. That functionality is in an internal class System.Web.HttpValueCollection that is returned by HttpUtility.ParseQueryString .

So you will not be able to achieve this behavior by using built in functionality. Your best bet would be to create an extension method that formats the values in a URL format.

Here is the method from HttpValueCollection class - you might be able to use it with some modifications.

// System.Web.HttpValueCollection
internal virtual string ToString(bool urlencoded, IDictionary excludeKeys)
{
    int count = this.Count;
    if (count == 0)
    {
        return string.Empty;
    }
    StringBuilder stringBuilder = new StringBuilder();
    bool flag = excludeKeys != null && excludeKeys["__VIEWSTATE"] != null;
    for (int i = 0; i < count; i++)
    {
        string text = this.GetKey(i);
        if ((!flag || text == null || !text.StartsWith("__VIEWSTATE", StringComparison.Ordinal)) && (excludeKeys == null || text == null || excludeKeys[text] == null))
        {
            if (urlencoded)
            {
                text = HttpValueCollection.UrlEncodeForToString(text);
            }
            string value = (text != null) ? (text + "=") : string.Empty;
            string[] values = this.GetValues(i);
            if (stringBuilder.Length > 0)
            {
                stringBuilder.Append('&');
            }
            if (values == null || values.Length == 0)
            {
                stringBuilder.Append(value);
            }
            else
            {
                if (values.Length == 1)
                {
                    stringBuilder.Append(value);
                    string text2 = values[0];
                    if (urlencoded)
                    {
                        text2 = HttpValueCollection.UrlEncodeForToString(text2);
                    }
                    stringBuilder.Append(text2);
                }
                else
                {
                    for (int j = 0; j < values.Length; j++)
                    {
                        if (j > 0)
                        {
                            stringBuilder.Append('&');
                        }
                        stringBuilder.Append(value);
                        string text2 = values[j];
                        if (urlencoded)
                        {
                            text2 = HttpValueCollection.UrlEncodeForToString(text2);
                        }
                        stringBuilder.Append(text2);
                    }
                }
            }
        }
    }
    return stringBuilder.ToString();
}

internal static string UrlEncodeForToString(string input)
{
    return HttpUtility.UrlEncodeUnicode(input);
}

Calling .ToString() on a name value collection will just give you the namespace it belongs to.

I suspect you want the key and value out of it, Assuming that it's the first in the collection why not just do:

var newUrl = String.Concat(_rootPath + "?" + nameValues.GetKey(0) + nameValues.Get(0));

You can have this as an extension method:

public static string ToString(this NameValueCollection nvc, int idx)
{
     if(nvc == null)
         throw new NullReferenceException();

     string key = nvc[idx];
     if(nvc.HasKeys() && !string.IsNullOrEmpty(key))
     {
         return string.Concat(key, nvc.Get(key)); //maybe want some formatting here
     }

     return string.Empty;
}

Usage:

NameValueCollection nvc = new NameValueCollection();
string foo = nvc.ToString(0); //gets key + value at index 0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM