Best way to parse URL string to get values for keys?
IosObjective CSwiftParsingCocoa TouchIos Problem Overview
I need to parse a URL string like this one:
&ad_eurl=http://www.youtube.com/video/4bL4FI1Gz6s&hl=it_IT&iv_logging_level=3&ad_flags=0&endscreen_module=http://s.ytimg.com/yt/swfbin/endscreen-vfl6o3XZn.swf&cid=241&cust_gender=1&avg_rating=4.82280613104
I need to split the NSString up into the signle parts like cid=241
and &avg_rating=4.82280613104
. I've been doing this with substringWithRange:
but the values return in a random order, so that messes it up. Is there any class that allows easy parsing where you can basically convert it to NSDictionary to be able to read the value for a key (for example ValueForKey:cid
should return 241
). Or is there just another easier way to parse it than using NSMakeRange
to get a substring?
Ios Solutions
Solution 1 - Ios
I also answered this at https://stackoverflow.com/a/26406478/215748.
You can use queryItems
in URLComponents
.
> When you get this property’s value, the NSURLComponents class parses the query string and returns an array of NSURLQueryItem objects, each of which represents a single key-value pair, in the order in which they appear in the original query string.
let url = "http://example.com?param1=value1¶m2=param2"
let queryItems = URLComponents(string: url)?.queryItems
let param1 = queryItems?.filter({$0.name == "param1"}).first
print(param1?.value)
Alternatively, you can add an extension on URL to make things easier.
extension URL {
var queryParameters: QueryParameters { return QueryParameters(url: self) }
}
class QueryParameters {
let queryItems: [URLQueryItem]
init(url: URL?) {
queryItems = URLComponents(string: url?.absoluteString ?? "")?.queryItems ?? []
print(queryItems)
}
subscript(name: String) -> String? {
return queryItems.first(where: { $0.name == name })?.value
}
}
You can then access the parameter by its name.
let url = URL(string: "http://example.com?param1=value1¶m2=param2")!
print(url.queryParameters["param1"])
Solution 2 - Ios
edit (June 2018): this answer is better. Apple added NSURLComponents
in iOS 7.
I would create a dictionary, get an array of the key/value pairs with
NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init];
NSArray *urlComponents = [urlString componentsSeparatedByString:@"&"];
Then populate the dictionary :
for (NSString *keyValuePair in urlComponents)
{
NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
NSString *key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
NSString *value = [[pairComponents lastObject] stringByRemovingPercentEncoding];
[queryStringDictionary setObject:value forKey:key];
}
You can then query with
[queryStringDictionary objectForKey:@"ad_eurl"];
This is untested, and you should probably do some more error tests.
Solution 3 - Ios
I'm a bit late, but the answers provided until now didn't work as I needed to. You can use this code snippet:
NSMutableDictionary *queryStrings = [[NSMutableDictionary alloc] init];
for (NSString *qs in [url.query componentsSeparatedByString:@"&"]) {
// Get the parameter name
NSString *key = [[qs componentsSeparatedByString:@"="] objectAtIndex:0];
// Get the parameter value
NSString *value = [[qs componentsSeparatedByString:@"="] objectAtIndex:1];
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
value = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
queryStrings[key] = value;
}
Where url
is the URL you want to parse. You have all of the query strings, escaped, in the queryStrings
mutable dictionary.
EDIT: Swift version:
var queryStrings = [String: String]()
if let query = url.query {
for qs in query.componentsSeparatedByString("&") {
// Get the parameter name
let key = qs.componentsSeparatedByString("=")[0]
// Get the parameter value
var value = qs.componentsSeparatedByString("=")[1]
value = value.stringByReplacingOccurrencesOfString("+", withString: " ")
value = value.stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
queryStrings[key] = value
}
}
Solution 4 - Ios
For iOS8 and above using NSURLComponents
:
+(NSDictionary<NSString *, NSString *> *)queryParametersFromURL:(NSURL *)url {
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
NSMutableDictionary<NSString *, NSString *> *queryParams = [NSMutableDictionary<NSString *, NSString *> new];
for (NSURLQueryItem *queryItem in [urlComponents queryItems]) {
if (queryItem.value == nil) {
continue;
}
[queryParams setObject:queryItem.value forKey:queryItem.name];
}
return queryParams;
}
For iOS 8 below:
+(NSDictionary<NSString *, NSString *> *)queryParametersFromURL:(NSURL *)url
NSMutableDictionary<NSString *, NSString *> * parameters = [NSMutableDictionary<NSString *, NSString *> new];
[self enumerateKeyValuePairsFromQueryString:url.query completionblock:^(NSString *key, NSString *value) {
parameters[key] = value;
}];
return parameters.copy;
}
- (void)enumerateKeyValuePairsFromQueryString:(NSString *)queryString completionBlock:(void (^) (NSString *key, NSString *value))block {
if (queryString.length == 0) {
return;
}
NSArray *keyValuePairs = [queryString componentsSeparatedByString:@"&"];
for (NSString *pair in keyValuePairs) {
NSRange range = [pair rangeOfString:@"="];
NSString *key = nil;
NSString *value = nil;
if (range.location == NSNotFound) {
key = pair;
value = @"";
}
else {
key = [pair substringToIndex:range.location];
value = [pair substringFromIndex:(range.location + range.length)];
}
key = [self decodedStringFromString:key];
key = key ?: @"";
value = [self decodedStringFromString:value];
value = value ?: @"";
block(key, value);
}
}
+ (NSString *)decodedStringFromString:(NSString *)string {
NSString *input = shouldDecodePlusSymbols ? [string stringByReplacingOccurrencesOfString:@"+" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, string.length)] : string;
return [input stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
Solution 5 - Ios
Swift 5
extension URL {
func queryParams() -> [String:String] {
let queryItems = URLComponents(url: self, resolvingAgainstBaseURL: false)?.queryItems
let queryTuples: [(String, String)] = queryItems?.compactMap{
guard let value = $0.value else { return nil }
return ($0.name, value)
} ?? []
return Dictionary(uniqueKeysWithValues: queryTuples)
}
}
Solution 6 - Ios
If you want to do the same thing in swift, you can use an extension.
extension NSURL {
func queryDictionary() -> [String:String] {
let components = self.query?.componentsSeparatedByString("&")
var dictionary = [String:String]()
for pairs in components ?? [] {
let pair = pairs.componentsSeparatedByString("=")
if pair.count == 2 {
dictionary[pair[0]] = pair[1]
}
}
return dictionary
}
}
Solution 7 - Ios
Since iOS 8 you can directly use properties name
and value
on NSURLQueryItem
.
Example, how to parse URL and get specific values for a key in parsed pairs.
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:@"someURL" resolvingAgainstBaseURL:false];
NSArray *queryItems = urlComponents.queryItems;
NSMutableArray *someIDs = [NSMutableArray new];
for (NSURLQueryItem *item in queryItems) {
if ([item.name isEqualToString:@"someKey"]) {
[someIDs addObject:item.value];
}
}
NSLog(@"%@", someIDs);
Solution 8 - Ios
If you're using NSURLComponents
the following concise extension will also do the trick:
extension NSURLComponents {
func getQueryStringParameter(name: String) -> String? {
return (self.queryItems? as [NSURLQueryItem])
.filter({ (item) in item.name == name }).first?
.value()
}
}
Solution 9 - Ios
-(NSArray *)getDataOfQueryString:(NSString *)url{
NSArray *strURLParse = [url componentsSeparatedByString:@"?"];
NSMutableArray *arrQueryStringData = [[NSMutableArray alloc] init];
if ([strURLParse count] < 2) {
return arrQueryStringData;
}
NSArray *arrQueryString = [[strURLParse objectAtIndex:1] componentsSeparatedByString:@"&"];
for (int i=0; i < [arrQueryString count]; i++) {
NSMutableDictionary *dicQueryStringElement = [[NSMutableDictionary alloc]init];
NSArray *arrElement = [[arrQueryString objectAtIndex:i] componentsSeparatedByString:@"="];
if ([arrElement count] == 2) {
[dicQueryStringElement setObject:[arrElement objectAtIndex:1] forKey:[arrElement objectAtIndex:0]];
}
[arrQueryStringData addObject:dicQueryStringElement];
}
return arrQueryStringData;
}
You this function just Pass URL and you will get all the element of querystring.
Solution 10 - Ios
This code is work in three cases
1.http://www.youtube.com/watch?v=VWsl7C-y7EI&feature=youtu.be
2.http://youtu.be/lOvcFqQyaDY
3.http://www.youtube.com/watch?v=VWsl7C-y7EI
NSArray *arr = [youtubeurl componentsSeparatedByString:@"v="];
NSString *youtubeID;
if([arr count]>0)
{
if([arr count]==1){
youtubeID= [[youtubeurl componentsSeparatedByString:@"/"] lastObject];
}
else{
NSArray *urlComponents = [[arr objectAtIndex:1] componentsSeparatedByString:@"&"];
youtubeID=[urlComponents objectAtIndex:0];
}
}
Solution 11 - Ios
A late solution to this in the form of a Swift 3 extension on URL
extension URL {
func value(for paramater: String) -> String? {
let queryItems = URLComponents(string: self.absoluteString)?.queryItems
let queryItem = queryItems?.filter({$0.name == paramater}).first
let value = queryItem?.value
return value
}
}
Solution 12 - Ios
As full function:
+ (NSString *)getQueryComponentWithName:(NSString *)name fromURL:(NSURL *)url{
NSString *component = nil;
if (url) {
NSString *query = url.query;
NSMutableDictionary *queryStringDictionary = [NSMutableDictionary dictionary];
NSArray *urlComponents = [query componentsSeparatedByString:@"&"];
for (NSString *keyValuePair in urlComponents){
NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
NSString *key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
NSString *value = [[pairComponents lastObject] stringByRemovingPercentEncoding];
[queryStringDictionary setObject:value forKey:key];
}
component = [queryStringDictionary objectForKey:name];
}
return component;
}
[self getQueryComponentWithName:@"example" fromURL:[NSURL URLWithString:@"https://google.es/?example=test"]];
Solution 13 - Ios
To get query parameters as a dict:
extension URL {
var parameters: [String: String] {
var parameters = [String: String]()
if let urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: false),
let queryItems = urlComponents.queryItems {
for queryItem in queryItems where queryItem.value != nil {
parameters[queryItem.name] = queryItem.value
}
}
return parameters
}
}
or return Optional if this more convenient in your case.
Solution 14 - Ios
Query property in the NSURL will give the query string. Then you can parse the query string using componentsSeparatedByString
NSArray *parameters = [[url query] componentsSeparatedByString:@"&"];
NSMutableDictionary *keyValuePairs = [NSMutableDictionary dictionary];
for (NSString *eachParam in parameters)
{
NSArray *QryParts = [eachParam componentsSeparatedByString:@"="];
if ( [QryParts count] == 2 )
{
keyValuePairs[QryParts[0]] = QryParts[1];
}
else
{
keyValuePairs[QryParts[0]] = QryParts[0];
}
}
NSString * name = [keyValuePairs valueForKey:@"name"];
NSString * username = [keyValuePairs valueForKey:@"username"];
Solution 15 - Ios
- (NSString *)getLoginTokenFromUrl:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSArray *queryStrings = [url.query componentsSeparatedByString:@"&"];
NSMutableDictionary *queryParams = [[NSMutableDictionary alloc] init];
for (NSString *qs in queryStrings) {
// Get the parameter name
NSArray *components = [qs componentsSeparatedByString:@"="];
NSString *key = [components objectAtIndex:0];
// Get the parameter value
NSString *value;
if (components.count > 1) {
value = [components objectAtIndex:1];
}
else {
value = @"";
}
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
value = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
queryParams[key] = value;
}
return [queryParams objectForKey:@"login_token"];
}
Solution 16 - Ios
A Swift 2 approach:
extension NSURL {
var queryDictionary: [String: String] {
var queryDictionary = [String: String]()
guard let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false), queryItems = components.queryItems else { return queryDictionary }
queryItems.forEach { queryDictionary[$0.name] = $0.value }
return queryDictionary
}
}