Some important optimizations from other answers:
- handling a # in the Uri
- handling possible missing value like &name= in the Uri
- not forgetting
Uri.UnescapeDataString
- returning a
Dictionary
instead of a IEnumerable
so one can find a desired parameter easily
- explaining that
Uri.Query
is unreliable on WP7
- comparing speed for best solution
With Regex (slower than Split):
static readonly Regex QueryStringRegex1 = new Regex(@"^[^#]*\?([^#]*)");
static readonly Regex QueryStringRegex2 = new Regex(@"(?<name>[^&=]+)=(?<value>[^&=]*)");
public static Dictionary<string, string> QueryDictionary(this Uri uri)
{
return QueryStringRegex1.Match(uri.ToString())// between ? and #
.Groups
.Cast<Group>()
.Select(a => QueryStringRegex2.Matches(a.Value)// between ? and &
.Cast<Match>()
.Select(b => b.Groups)
.ToDictionary(b => Uri.UnescapeDataString(b["name"].Value), b => Uri.UnescapeDataString(b["value"].Value)))
.ElementAtOrDefault(1)
?? new Dictionary<string, string>();
}
With Split (faster than Regex, recommended solution):
static readonly char[] QueryStringSeparator1 = "#".ToCharArray();
static readonly char[] QueryStringSeparator2 = "?".ToCharArray();
static readonly char[] QueryStringSeparator3 = "&".ToCharArray();
static readonly char[] QueryStringSeparator4 = "=".ToCharArray();
public static Dictionary<string, string> QueryDictionary(this Uri uri)
{
return uri.ToString()
.Split(QueryStringSeparator1, StringSplitOptions.RemoveEmptyEntries)
.Select(a => a.Split(QueryStringSeparator2, StringSplitOptions.RemoveEmptyEntries)
.Select(b => b.Split(QueryStringSeparator3, StringSplitOptions.RemoveEmptyEntries)
.Select(c => c.Split(QueryStringSeparator4))
.Where(c => c[0].Length > 0)
.ToDictionary(c => Uri.UnescapeDataString(c[0]), c => c.Length > 1 ? Uri.UnescapeDataString(c[1]) : ""))
.ElementAtOrDefault(1))// after ?
.FirstOrDefault()// before #
?? new Dictionary<string, string>();
}
Note: on a Windows Phone 7 device, Uri.Query
is unreliable for an Uri like "mailto:a@example.com?subject=subject&body=body". That's why we use Uri.ToString()
(itself better than Uri.OriginalString
). If you only want to handle "(http|https)://" or you want to optimize for a Windows Phone 8 device, you can find an optimized solution on https://stackoverflow.com/a/25167971/1033581.
If you want this Uri.Query
optimization on a Windows Phone 8 device using a Windows Phone 7 app, you can check beforehand:
public static readonly bool IsVersion8 = Environment.OSVersion.Version >= new Version(8, 0);