ObjectiveC'de NSDictionary nesnelerinden URL sorgu parametreleri oluşturma


90

Standart Kakao kitaplıklarında (NSURL, NSMutableURL, NSMutableURLRequest, vb.) Bulunan tüm URL işleme nesneleriyle, bir GET isteğini programlı olarak oluşturmanın kolay bir yolunu gözden kaçırmam gerektiğini biliyorum.

Şu anda manuel olarak "?" ardından "&" ile birleştirilen ad değer çiftleri geliyor, ancak tüm ad ve değer çiftlerimin manuel olarak kodlanması gerekiyor, böylece NSMutableURLRequest, URL'ye bağlanmaya çalıştığında tamamen başarısız olmaz.

Bu, .... için önceden pişirilmiş bir API kullanabilmem gereken bir şey gibi hissettiriyor ... bir NSURL'ye sorgu parametrelerinin NSDictionary'sini eklemek için kutunun dışında bir şey var mı? Buna yaklaşmam gereken başka bir yol var mı?


Çok fazla cevap! Soru ve cevaplarda çok fazla oy var! Ve yine de işaretlenmemiş yanıt. Woah!
BangOperator

Yanıtlar:


132

NSURLQueryItemSorgu oluşturmak için kullanılabilen iOS8 ve OS X 10.10'da sunulmuştur . NSURLQueryItem üzerindeki belgelerden :

NSURLQueryItem nesnesi, bir URL'nin sorgu kısmındaki bir öğe için tek bir ad / değer çiftini temsil eder. NSURLComponents nesnesinin queryItems özelliğiyle sorgu öğelerini kullanırsınız.

Bir tane oluşturmak için belirlenen başlatıcıyı kullanın queryItemWithName:value:ve ardından bunları NSURLComponentsbir NSURL. Örneğin:

NSURLComponents *components = [NSURLComponents componentsWithString:@"http://stackoverflow.com"];
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

Soru işareti ve ve işaretinin otomatik olarak işlendiğine dikkat edin. Bir NSURLparametre sözlüğünden bir parametre oluşturmak, şu kadar basittir:

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

Ayrıca ve ile URL'lerin nasıl oluşturulacağına dair bir blog yazısı da yazdım .NSURLComponentsNSURLQueryItems


2
ya sözlük iç içe geçmişse?
hariszaman

1
@hariszaman, URL aracılığıyla iç içe geçmiş bir sözlük gönderiyorsanız, muhtemelen bunun yerine POST gövdesi aracılığıyla göndermeyi düşünmelisiniz.
Joe Masilotti

2
sadece not etmek
gerekirse

Burada bir noktayı vurgulamak istiyorum: NSURLQueryItem için değer yalnızca bir dizge olabilir. Durum böyle değilse bir çökmeye neden olabilir!
Pulkit Sharma

47

Bunu NSDictionaryyapmak için bir kategori oluşturabilirsiniz - Cocoa kütüphanesinde benim de bulabileceğim standart bir yol yok. Kullandığım kod şuna benziyor:

// file "NSDictionary+UrlEncoding.h"
#import <cocoa/cocoa.h>

@interface NSDictionary (UrlEncoding)

-(NSString*) urlEncodedString;

@end

bu uygulama ile:

// file "NSDictionary+UrlEncoding.m"
#import "NSDictionary+UrlEncoding.h"

// helper function: get the string form of any object
static NSString *toString(id object) {
  return [NSString stringWithFormat: @"%@", object];
}

// helper function: get the url encoded string form of any object
static NSString *urlEncode(id object) {
  NSString *string = toString(object);
  return [string stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
}

@implementation NSDictionary (UrlEncoding)

-(NSString*) urlEncodedString {
  NSMutableArray *parts = [NSMutableArray array];
  for (id key in self) {
    id value = [self objectForKey: key];
    NSString *part = [NSString stringWithFormat: @"%@=%@", urlEncode(key), urlEncode(value)];
    [parts addObject: part];
  }
  return [parts componentsJoinedByString: @"&"];
}

@end

Kodun oldukça basit olduğunu düşünüyorum, ancak bunu http://blog.ablepear.com/2008/12/urlencoding-category-for-nsdictionary.html adresinde biraz daha ayrıntılı olarak tartışıyorum .


16
Bunun işe yaradığını sanmıyorum. Özellikle, ve işaretleri yanı sıra belirtilen sorgu parametrelerinde öncelenmelidir diğer karakterlerden kurtulmak etmeyen, stringByAddingPercentEscapesUsingEncoding kullandığınız RFC 3986 . Bunun yerine Mac için Google Araç Kutusu kaçan koduna bakmayı düşünün .
Dave Peck

Bu, düzgün kaçış için çalışır: stackoverflow.com/questions/9187316/…
JoeCortopassi

30

Chris'in cevabını kullanmak istedim, ancak Otomatik Referans Sayma (ARC) için yazılmadığı için onu güncelledim. Başka birinin aynı sorunu yaşaması durumunda çözümümü yapıştıracağımı düşündüm. Not:self uygun olduğunda örnek veya sınıf adıyla değiştirin .

+(NSString*)urlEscapeString:(NSString *)unencodedString 
{
    CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString;
    NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ", kCFStringEncodingUTF8);
    CFRelease(originalStringRef);
    return s;
}


+(NSString*)addQueryStringToUrlString:(NSString *)urlString withDictionary:(NSDictionary *)dictionary
{
    NSMutableString *urlWithQuerystring = [[NSMutableString alloc] initWithString:urlString];

    for (id key in dictionary) {
        NSString *keyString = [key description];
        NSString *valueString = [[dictionary objectForKey:key] description];

        if ([urlWithQuerystring rangeOfString:@"?"].location == NSNotFound) {
            [urlWithQuerystring appendFormat:@"?%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
        } else {
            [urlWithQuerystring appendFormat:@"&%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
        }
    }
    return urlWithQuerystring;
}

Bu kodu beğendim, eklenmesi gereken tek şey kodlamanın tüm anahtar / değerler için url güvenli olması.
Pellet

22

Değerler dizeler ise diğer yanıtlar harika çalışır, ancak değerler sözlük veya dizilerse bu kod bunu halleder.

Sorgu dizesi yoluyla bir dizi / sözlüğü geçirmenin standart bir yolu olmadığını unutmamak önemlidir, ancak PHP bu çıktıyı gayet iyi işler.

-(NSString *)serializeParams:(NSDictionary *)params {
    /*

     Convert an NSDictionary to a query string

     */

    NSMutableArray* pairs = [NSMutableArray array];
    for (NSString* key in [params keyEnumerator]) {
        id value = [params objectForKey:key];
        if ([value isKindOfClass:[NSDictionary class]]) {
            for (NSString *subKey in value) {
                NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
                                                                                              (CFStringRef)[value objectForKey:subKey],
                                                                                              NULL,
                                                                                              (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                                                                                              kCFStringEncodingUTF8);
                [pairs addObject:[NSString stringWithFormat:@"%@[%@]=%@", key, subKey, escaped_value]];
            }
        } else if ([value isKindOfClass:[NSArray class]]) {
            for (NSString *subValue in value) {
                NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
                                                                                              (CFStringRef)subValue,
                                                                                              NULL,
                                                                                              (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                                                                                              kCFStringEncodingUTF8);
                [pairs addObject:[NSString stringWithFormat:@"%@[]=%@", key, escaped_value]];
            }
        } else {
            NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
                                                                                          (CFStringRef)[params objectForKey:key],
                                                                                          NULL,
                                                                                          (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                                                                                          kCFStringEncodingUTF8);
            [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, escaped_value]];
            [escaped_value release];
        }
    }
    return [pairs componentsJoinedByString:@"&"];
}

Örnekler

[foo] => bar
[translations] => 
        {
            [one] => uno
            [two] => dos
            [three] => tres
        }

foo = bar & çeviriler [bir] = uno & çeviriler [iki] = dos ve çeviriler [üç] = tres

[foo] => bar
[translations] => 
        {
            uno
            dos
            tres
        }

foo = bar ve çeviriler [] = uno ve çeviriler [] = dos ve çeviriler [] = tres


1
Son 'else' durumu için, bir uzunluk çağrısında engelleme yerine NSNumber için dizeler sağlayacak şekilde açıklamaya bir çağrı ekledim: '(CFStringRef) [[params objectForKey: key] description ]' Teşekkürler!
JohnQ

mükemmel cevap. Ancak CFURLCreateStringByAddingPercentEscapesçağrılarıstringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]
S1LENT WARRIOR

8

AlBeebe tarafından yeniden düzenlendim ve ARC cevabına çevirdim

- (NSString *)serializeParams:(NSDictionary *)params {
    NSMutableArray *pairs = NSMutableArray.array;
    for (NSString *key in params.keyEnumerator) {
        id value = params[key];
        if ([value isKindOfClass:[NSDictionary class]])
            for (NSString *subKey in value)
                [pairs addObject:[NSString stringWithFormat:@"%@[%@]=%@", key, subKey, [self escapeValueForURLParameter:[value objectForKey:subKey]]]];

        else if ([value isKindOfClass:[NSArray class]])
            for (NSString *subValue in value)
            [pairs addObject:[NSString stringWithFormat:@"%@[]=%@", key, [self escapeValueForURLParameter:subValue]]];

        else
            [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, [self escapeValueForURLParameter:value]]];

}
return [pairs componentsJoinedByString:@"&"];

}

- (NSString *)escapeValueForURLParameter:(NSString *)valueToEscape {
     return (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) valueToEscape,
             NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
}

2
Bu benim için harika çalıştı, ancak son satırı bir? ilk parametreden önce:[NSString stringWithFormat:@"?%@",[pairs componentsJoinedByString:@"&"]];
BFar

1
CFURLCreateStringByAddingPercentEscapesçağrıları değiştirmeniz gerektiğini düşünüyorumstringByAddingPercentEncodingWithAllowedCharacters:[NSCharac‌​terSet URLQueryAllowedCharacterSet]
S1LENT WARRIOR

5

Zaten AFNetworking kullanıyorsanız (benim durumumda olduğu gibi), gerekli olanıAFHTTPRequestSerializer oluşturmak için onun sınıfını kullanabilirsiniz NSURLRequest.

[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:@"YOUR_URL" parameters:@{PARAMS} error:nil];

Yalnızca işiniz için URL'ye ihtiyacınız varsa, kullanın NSURLRequest.URL.


3

İşte Swift (iOS8 +) için basit bir örnek :

private let kSNStockInfoFetchRequestPath: String = "http://dev.markitondemand.com/Api/v2/Quote/json"

private func SNStockInfoFetchRequestURL(symbol:String) -> NSURL? {
  if let components = NSURLComponents(string:kSNStockInfoFetchRequestPath) {
    components.queryItems = [NSURLQueryItem(name:"symbol", value:symbol)]
    return components.URL
  }
  return nil
}

1
Oldukça temiz, ancak queryItemsiOS8.0'dan edinilebilir.
Adil Soomro

Teşekkürler, bunu netleştirmek için bir yorum ekledi.
Zorayr

3

Joel'in kullanma tavsiyesini aldım URLQueryItemsve Swift Extension'a dönüştüm (Swift 3)

extension URL
{
    /// Creates an NSURL with url-encoded parameters.
    init?(string : String, parameters : [String : String])
    {
        guard var components = URLComponents(string: string) else { return nil }

        components.queryItems = parameters.map { return URLQueryItem(name: $0, value: $1) }

        guard let url = components.url else { return nil }

        // Kinda redundant, but we need to call init.
        self.init(string: url.absoluteString)
    }
}

( self.initYöntem biraz sevimsiz, ancak NSURLbileşenlerle başlangıç yoktu )

Olarak kullanılabilir

URL(string: "http://www.google.com/", parameters: ["q" : "search me"])

2

Başka bir çözümüm var:

http://splinter.com.au/build-a-url-query-string-in-obj-c-from-a-dict

+(NSString*)urlEscape:(NSString *)unencodedString {
    NSString *s = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
        (CFStringRef)unencodedString,
        NULL,
        (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
        kCFStringEncodingUTF8);
    return [s autorelease]; // Due to the 'create rule' we own the above and must autorelease it
}

// Put a query string onto the end of a url
+(NSString*)addQueryStringToUrl:(NSString *)url params:(NSDictionary *)params {
    NSMutableString *urlWithQuerystring = [[[NSMutableString alloc] initWithString:url] autorelease];
    // Convert the params into a query string
    if (params) {
        for(id key in params) {
            NSString *sKey = [key description];
            NSString *sVal = [[params objectForKey:key] description];
            // Do we need to add ?k=v or &k=v ?
            if ([urlWithQuerystring rangeOfString:@"?"].location==NSNotFound) {
                [urlWithQuerystring appendFormat:@"?%@=%@", [Http urlEscape:sKey], [Http urlEscape:sVal]];
            } else {
                [urlWithQuerystring appendFormat:@"&%@=%@", [Http urlEscape:sKey], [Http urlEscape:sVal]];
            }
        }
    }
    return urlWithQuerystring;
}

Daha sonra bunu şu şekilde kullanabilirsiniz:

NSDictionary *params = @{@"username":@"jim", @"password":@"abc123"};

NSString *urlWithQuerystring = [self addQueryStringToUrl:@"https://myapp.com/login" params:params];

4
Lütfen cevabınızda ilgili kodu gönderin, bu bağlantı sonsuza kadar sürmeyebilir.
Madbreaks

2
-(NSString*)encodeDictionary:(NSDictionary*)dictionary{
    NSMutableString *bodyData = [[NSMutableString alloc]init];
    int i = 0;
    for (NSString *key in dictionary.allKeys) {
        i++;
        [bodyData appendFormat:@"%@=",key];
        NSString *value = [dictionary valueForKey:key];
        NSString *newString = [value stringByReplacingOccurrencesOfString:@" " withString:@"+"];
        [bodyData appendString:newString];
        if (i < dictionary.allKeys.count) {
            [bodyData appendString:@"&"];
        }
    }
    return bodyData;
}

Bu, adlardaki veya değerlerdeki özel karakterleri (boşluk dışında) doğru şekilde kodlamaz.
jcaron


1

Eğer kullanırsanız Yine başka bir çözüm, RestKit bir işlev var RKURLEncodedSerializationdenilen RKURLEncodedStringFromDictionaryWithEncodingtam olarak ne istediğinizi yapar.


1

Objective-c'de NSDictionary'yi url sorgu dizesine dönüştürmenin basit yolu

Ör: first_name = Steve & middle_name = Gates & last_name = İşler ve adres = Palo Alto, California

    NSDictionary *sampleDictionary = @{@"first_name"         : @"Steve",
                                     @"middle_name"          : @"Gates",
                                     @"last_name"            : @"Jobs",
                                     @"address"              : @"Palo Alto, California"};

    NSMutableString *resultString = [NSMutableString string];
            for (NSString* key in [sampleDictionary allKeys]){
                if ([resultString length]>0)
                    [resultString appendString:@"&"];
                [resultString appendFormat:@"%@=%@", key, [sampleDictionary objectForKey:key]];
            }
NSLog(@"QueryString: %@", resultString);

Umut yardımcı olacaktır :)


Bu yanlıştır çünkü sözlük değerlerinde görünürlerse "&" gibi karakterlerden çıkış yapmaz.
Thomas Tempelmann
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.