Разобрать путь и запрос NSURL на iOS

Существуют ли какие-либо стандартные объекты или функции для анализа компонентов NSURL? Конечно, я мог бы написать один, но зачем изобретать велосипед?

[NSURL path] вернет NSString как "argX=x&argY=y&argZ=z" Что я бы предпочел вернуть, так это словарь, заполненный {@"argX" => @"x", @"argY" => @"y", @"argZ" = @"z"}

Для пути, который возвращает строку типа "/partA/partB/partC", я бы предпочел получить массив со структурой {[0] => @"partA", [1] => @"partB", [2] => @"partC"}

Я понимаю, что это довольно специфический вопрос, но похоже, что многие люди хотели бы.

Это для iOS! По-видимому, NSURL имеет другие функции в macOS.


person DougW    schedule 27.12.2009    source источник
comment
Хороший вопрос. Вы имеете в виду: если URL-адрес /partA/partB/partC, вам нужно 0 => часть A, 1 => часть B, 2 => часть C. Если URL-адрес /foo?partA=A&partB=B, вы хотите, чтобы partA =› A, partB =› B. Что, если URL-адрес /partA/partB/partC?foo=bar&ooga=booga? В одном примере важная информация, необходимая для идентификации рассматриваемого объекта, передается в строке запроса... в другом примере идентифицирующая информация передается в стиле покоя в самом пути.   -  person Jarret Hardie    schedule 28.12.2009
comment
Я имею в виду, что хочу разобрать путь и запрос отдельно. Это не должна быть одна функция, которая объединяет эти две задачи. Смотрите код, который я разместил ниже, для пояснений. Если кто-то ответит существующей реализацией, я буду рад принять это.   -  person DougW    schedule 28.12.2009
comment
Следует иметь в виду, что это совершенно допустимая строка запроса: x=1&x=0&x=foo, поэтому NSDictionary может быть не лучшим представлением (если только вы не используете массивы в качестве значений).   -  person n8gray    schedule 15.03.2011
comment
@ n8gray - Вы правы, мой ответ ниже не касается повторяющихся значений. Однако я бы избегал повторяющихся значений, таких как чума, в любых реальных сценариях, потому что многие серверы (например, серверы PHP) будут иметь только одно значение для x... x=>foo, как и мой сценарий. Однако PHP будет строить массив из x[]=1&x[]=foo, чего мой ответ не сделает. Это должно быть тривиальным упражнением, чтобы расширить мой пример для построения массивов, если имя левого параметра заканчивается на [], поэтому я оставлю это для тех, кому это нужно добавить. Спасибо!   -  person DougW    schedule 17.03.2011


Ответы (6)


Возможно, вы захотите взглянуть на pathComponents, который возвращает массив компонентов URL. Получите дополнительную информацию здесь.

person Mike    schedule 27.12.2009
comment
Я не верю, что это доступно в версии SDK для iphone. Я должен был уточнить, что я работаю над iPhone OS. - person DougW; 28.12.2009
comment
[NSString pathComponents] доступен как в Cocoa, так и в Cocoa Touch API. На iPhone вы можете имитировать OS X 10.6 [NSURL pathComponents] с помощью [[NSURL path] pathComponents]. - person Jarret Hardie; 28.12.2009
comment
Ах, отлично, это то, что я искал, по крайней мере, для части пути! - person DougW; 30.12.2009
comment
Документы Apple для состояния [NSString pathComponents]: обратите внимание, что этот метод работает только с путями к файлам (например, не со строковыми представлениями URL-адресов). - person Jason Moore; 26.01.2010
comment
@JasonMoore, вот почему вы передаете ему вывод -path, а не необработанную строку URL. - person Mike Abdullah; 13.04.2013
comment
@MikeAbdullah: А, хорошо. Для всех, кто наткнулся на этот трехлетний вопрос: начиная с iOS4 вам больше не нужно преобразовывать URL-адрес в строку, и вы можете просто использовать: -[NSURL pathComponents] напрямую. - person Jason Moore; 15.04.2013
comment
pathComponents отлично работает для URL-адресов RFC 1808, однако, если вы анализируете mailto, он вернет ноль. - person Chris Stephens; 31.07.2013

Хорошо, я забеспокоился и написал решение для расширения NSString через категории. Я еще не проверял это, но если вы хотите использовать это, сделайте это.

@interface NSString (ParseCategory)
- (NSMutableDictionary *)explodeToDictionaryInnerGlue:(NSString *)innerGlue outterGlue:(NSString *)outterGlue;
@end

@implementation NSString (ParseCategory)

- (NSMutableDictionary *)explodeToDictionaryInnerGlue:(NSString *)innerGlue outterGlue:(NSString *)outterGlue {
    // Explode based on outter glue
    NSArray *firstExplode = [self componentsSeparatedByString:outterGlue];
    NSArray *secondExplode;

    // Explode based on inner glue
    NSInteger count = [firstExplode count];
    NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithCapacity:count];
    for (NSInteger i = 0; i < count; i++) {
        secondExplode = [(NSString *)[firstExplode objectAtIndex:i] componentsSeparatedByString:innerGlue];
        if ([secondExplode count] == 2) {
            [returnDictionary setObject:[secondExplode objectAtIndex:1] forKey:[secondExplode objectAtIndex:0]];
        }
    }

    return returnDictionary;
}

@end

Это называется так:

NSMutableDictionary *parsedQuery = [[myNSURL query] explodeToDictionaryInnerGlue:@"=" outterGlue=@"&"]

Для синтаксического анализа части пути NSURL (т.е. @"/partA/partB/partC") просто вызовите это:

NSArray *parsedPath = [[nyNSURL path] componentsSeperatedByString:@"/"];

Имейте в виду, что parsedPath[0] будет пустой строкой из-за ведущего /!

EDIT. Вот расширение категории для NSURL для вашего удовольствия. Он удаляет начальный «/», чтобы у вас не было пустого индекса 0.

@implementation NSURL (ParseCategory)

- (NSArray *)pathArray {
    // Create a character set for the slash character
    NSRange slashRange;
    slashRange.location = (unsigned int)'/';
    slashRange.length = 1;
    NSCharacterSet *slashSet = [NSCharacterSet characterSetWithRange:slashRange];

    // Get path with leading (and trailing) slashes removed
    NSString *path = [[self path] stringByTrimmingCharactersInSet:slashSet];

    return [path componentsSeparatedByCharactersInSet:slashSet];
}

- (NSDictionary *)queryDictionary {
    NSDictionary *returnDictionary = [[[[self query] explodeToDictionaryInnerGlue:@"=" outterGlue:@"&"] copy] autorelease];
    return returnDictionary;
}

@end
person DougW    schedule 28.12.2009
comment
Не уверен, почему кто-то проголосовал за это... думаю, это научит меня делиться кодом? Я бы не возражал, если бы вы оставили комментарий, но я просто предположу, что вы случайный тролль. Привет тролль. - person DougW; 30.12.2009
comment
Спасибо, это отлично работает для меня. Обратите внимание, что вам также может понадобиться декодировать URL-адреса ваших отдельных значений строки запроса. Я использую для этого следующий код, сначала чтобы позаботиться о процентном экранировании, а затем заменить + пробелом, чего, к сожалению, не делает первый: - person Mirko Froehlich; 13.01.2010
comment
Добро пожаловать. И да, вопросы кодирования/декодирования я оставил вне этих функций. - person DougW; 14.01.2010

Сэм Соффес создал хорошо поддерживаемую категорию для NSURL/NSDictionary. Его можно найти здесь: https://github.com/samsoffes/sstoolkit/.

person Klaas    schedule 30.05.2011

Вот что я использую для анализа строки запроса

// Parse the individual parameters
// parameters = @"hello=world&foo=bar";
NSMutableDictionary *dictParameters = [[NSMutableDictionary alloc] init];
NSArray *arrParameters = [parameters componentsSeparatedByString:@"&"];
for (int i = 0; i < [arrParameters count]; i++) {
    NSArray *arrKeyValue = [[arrParameters objectAtIndex:i] componentsSeparatedByString:@"="];
    if ([arrKeyValue count] >= 2) {
        NSMutableString *strKey = [NSMutableString stringWithCapacity:0];
        [strKey setString:[[[arrKeyValue objectAtIndex:0] lowercaseString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
        NSMutableString *strValue   = [NSMutableString stringWithCapacity:0];
        [strValue setString:[[[arrKeyValue objectAtIndex:1]  stringByReplacingOccurrencesOfString:@"+" withString:@" "] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
        if (strKey.length > 0) [dictParameters setObject:strValue forKey:strKey];
    }
}
NSLog(@"Parameters: %@", dictParameters);
person AlBeebe    schedule 08.12.2011

Если вы решите написать его (я не уверен, что существуют методы получения нужных вам компонентов), вы можете использовать componentsSeparatedByString из NSString.

person Jorge Israel Peña    schedule 27.12.2009

В iOS 8 и OS X 10.10 появился NSURLQueryItem, который можно использовать для создания запросов. Из документов на NSURLQueryItem:

Объект NSURLQueryItem представляет одну пару имя/значение для элемента в части запроса URL-адреса. Вы используете элементы запроса со свойством queryItems объекта NSURLComponents.

Вы можете получить элементы запроса из URL-адреса, сначала создав NSURLComponents:

NSURL *url = [NSURL URLWithString:@"http://stackoverflow.com?q=ios&count=10"];
NSURLComponents *components = [NSURLComponents componentsWithURL:url 
                                               resolvingAgainstBaseURL:YES];
for (NSURLQueryItem *item in components.queryItems) {
    NSLog(@"name: %@, value: %@", item.name, item.value);
}
// name: q, value: ios
// name: count, value: 10

Обратите внимание, что они возвращают значение для -queryItems, это массив, а не словарь. Это связано с тем, что следующий URL-адрес является допустимым. Обратите внимание на два одинаковых «ключа», foo.

http://google.com?foo=bar&foo=baz

Чтобы создать URL-адрес с помощью элементов запроса, используйте назначенный инициализатор queryItemWithName:value:, а затем добавьте их в NSURLComponents для создания файла NSURL. Например:

NSString *urlString = @"http://stackoverflow.com";
NSURLComponents *components = [NSURLComponents componentsWithString:urlString];
NSURLQueryItem *search = [NSURLQueryItem queryItemWithName:@"q" value:@"ios"];
NSURLQueryItem *count = [NSURLQueryItem queryItemWithName:@"count" value:@"10"];
components.queryItems = @[ search, count ];
NSURL *url = components.URL; // http://stackoverflow.com?q=ios&count=10

Обратите внимание, что вопросительный знак и амперсанд обрабатываются автоматически. Создать NSURL из словаря параметров так же просто, как:

NSDictionary *queryDictionary = @{ @"q": @"ios", @"count": @"10" };
NSMutableArray *queryItems = [NSMutableArray array];
for (NSString *key in queryDictionary) {
    NSURLQueryItem *item = [NSURLQueryItem queryItemWithName:key 
                                                       value:queryDictionary[key]]; 
    [queryItems addObject:item];
}
components.queryItems = queryItems;

Я также написал сообщение в блоге с более подробной информацией, Создание NSURL с помощью NSURLQueryItems.

person Joe Masilotti    schedule 28.07.2015
comment
Прохладный! Просто примечание для разработчиков - если вы используете повторяющиеся параметры GET, вас ждет мир боли. Не существует определенной спецификации того, как с ними обращаться, и разные языки/платформы составляют свои собственные правила. См. stackoverflow.com /вопросы/1746507/ - person DougW; 06.08.2015