NSURLクエリプロパティを解析する


82

私は次のようなURLを持っています myApp://action/1?parameter=2&secondparameter=3

プロパティクエリを使用すると、次の部分が得られます URL

parameter=2&secondparameter=3

これをNSDictionaryまたはに簡単に入れる方法はありますArrayか?

ありがとう

回答:


54

そんな感じ:

NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
for (NSString *param in [url componentsSeparatedByString:@"&"]) {
  NSArray *elts = [param componentsSeparatedByString:@"="];
  if([elts count] < 2) continue;
  [params setObject:[elts lastObject] forKey:[elts firstObject]];
}

:これはサンプルコードです。すべてのエラーケースが管理されているわけではありません。


2
このコードは、不正な形式のクエリの場合にクラッシュします。以下の安全な解決策を参照してください。
BadPirate 2011年

2
さて、配列サイズのチェックを追加するだけです!このコードはほんの始まりにすぎませんでした(いつものようにSOで)。あなたはすべてのURLに準拠するようにしたい場合は、あなたは、フラグメント(末尾の#部分)御馳走エスケープ文字列に多くの作品を持って、...
ブノワ

あなたのために編集を入れてください。答えのある問題に注意を向けるために反対票を投じただけです。編集が完了したら、元に戻すことができます。
BadPirate 2011年

1
このコードは、特殊文字を含まない最も単純なケースの値を除いて、正確に近いものではありません。
ニックスナイダー

代わりに、最後と最初のオブジェクトを使用する必要があります。 [params setObject:[elts lastObject] forKey:[elts firstObject]];より安全です
Laszlo 2015年

147

あなたは使用することができますqueryItemsの中でURLComponents

このプロパティの値を取得すると、NSURLComponentsクラスはクエリ文字列を解析し、NSURLQueryItemオブジェクトの配列を返します。各オブジェクトは、元のクエリ文字列に表示される順序で、単一のキーと値のペアを表します。

迅速

let url = "http://example.com?param1=value1&param2=param2"
let queryItems = URLComponents(string: url)?.queryItems
let param1 = queryItems?.filter({$0.name == "param1"}).first
print(param1?.value)

または、URLに拡張子を追加して、作業を簡単にすることもできます。

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
    }
}

その後、名前でパラメータにアクセスできます。

let url = "http://example.com?param1=value1&param2=param2"
print(url.queryParameters["param1"])

11
iOS7以降のクエリ。iOS8以降のqueryItems。
オナト2014

この答えをありがとう。これに加えて、NSURLComponentsの詳細については、次の記事が非常に役立つことがわかりました。nshipster.com/ nsurl
xaphod

素晴らしい答え。ありがとう!
dpigera 2016

不思議なことに、URLに#文字が含まれていると、NSURLComponentsは機能しません。同様 http://example.com/abc#abc?param1=value1&param2=param2 queryItemsはnilになります
ペドロ

これは、フラグメント(#abc)が最後に属しているためです。
オナト

55

私には、この動作に役立つ拡張機能をいくつか書く理由がありました。最初のヘッダー:

#import <Foundation/Foundation.h>

@interface NSString (XQueryComponents)
- (NSString *)stringByDecodingURLFormat;
- (NSString *)stringByEncodingURLFormat;
- (NSMutableDictionary *)dictionaryFromQueryComponents;
@end

@interface NSURL (XQueryComponents)
- (NSMutableDictionary *)queryComponents;
@end

@interface NSDictionary (XQueryComponents)
- (NSString *)stringFromQueryComponents;
@end

これらのメソッドはNSString、NSURL、およびNSDictionaryを拡張して、クエリコンポーネントの文字列および結果を含むディクショナリオブジェクトとの間で変換できるようにします。

次に、関連する.mコード:

#import "XQueryComponents.h"

@implementation NSString (XQueryComponents)
- (NSString *)stringByDecodingURLFormat
{
    NSString *result = [self stringByReplacingOccurrencesOfString:@"+" withString:@" "];
    result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    return result;
}

- (NSString *)stringByEncodingURLFormat
{
    NSString *result = [self stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    result = [result stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    return result;
}

- (NSMutableDictionary *)dictionaryFromQueryComponents
{
    NSMutableDictionary *queryComponents = [NSMutableDictionary dictionary];
    for(NSString *keyValuePairString in [self componentsSeparatedByString:@"&"])
    {
        NSArray *keyValuePairArray = [keyValuePairString componentsSeparatedByString:@"="];
        if ([keyValuePairArray count] < 2) continue; // Verify that there is at least one key, and at least one value.  Ignore extra = signs
        NSString *key = [[keyValuePairArray objectAtIndex:0] stringByDecodingURLFormat];
        NSString *value = [[keyValuePairArray objectAtIndex:1] stringByDecodingURLFormat];
        NSMutableArray *results = [queryComponents objectForKey:key]; // URL spec says that multiple values are allowed per key
        if(!results) // First object
        {
            results = [NSMutableArray arrayWithCapacity:1];
            [queryComponents setObject:results forKey:key];
        }
        [results addObject:value];
    }
    return queryComponents;
}
@end

@implementation NSURL (XQueryComponents)
- (NSMutableDictionary *)queryComponents
{
    return [[self query] dictionaryFromQueryComponents];
}
@end

@implementation NSDictionary (XQueryComponents)
- (NSString *)stringFromQueryComponents
{
    NSString *result = nil;
    for(__strong NSString *key in [self allKeys])
    {
        key = [key stringByEncodingURLFormat];
        NSArray *allValues = [self objectForKey:key];
        if([allValues isKindOfClass:[NSArray class]])
            for(__strong NSString *value in allValues)
            {
                value = [[value description] stringByEncodingURLFormat];
                if(!result)
                    result = [NSString stringWithFormat:@"%@=%@",key,value];
                else 
                    result = [result stringByAppendingFormat:@"&%@=%@",key,value];
            }
        else {
            NSString *value = [[allValues description] stringByEncodingURLFormat];
            if(!result)
                result = [NSString stringWithFormat:@"%@=%@",key,value];
            else 
                result = [result stringByAppendingFormat:@"&%@=%@",key,value];
        }
    }
    return result;
}
@end

2つの問題:1)キーと値の両方を辞書に保存する前にURLデコードする必要があり、2)URLの規則に従って、同じキーを複数回指定することが許可されています。IOW、単一の値ではなく、値の配列にマップされたキーである必要があります。
Dave DeLong 2011年

わかりました、配列サイズのチェックを追加するだけです(私の答えに反対票が必要ですか?)!このコードはほんの始まりにすぎませんでした(いつものようにSOで)。あなたはすべてのURLに準拠するようにしたい場合は、あなたは、フラグメント(末尾の#部分)御馳走エスケープ文字列に多くの作品を持って、...
ブノワ・

デイブ-良い点。私のニーズには必要ありませんでしたが、この一般的な問題にアクセスするすべての人に役立つ回答を作成しようとしているので、前述の機能を含めました...ブノワ-あなたの回答で問題に注意を喚起するだけです、編集を送信しました。おそらく、他の点では良い答えでマイナーな問題に注意を喚起するためにダウンを使用するのに最適な形式ではないでしょう、私は将来そうすることを控えます..編集が
完了

stringByDecodingURLFormatでは、「result」のタイプはNSString *である必要があります。
ThomasW 2011

別の問題に気づきました。キーと値が混同されています。キーはobjectAtIndex:0で、値はobjectAtIndex:1である必要があります。
ThomasW 2011

13

これを試して ;)!

NSString *query = @"parameter=2&secondparameter=3"; // replace this with [url query];
NSArray *components = [query componentsSeparatedByString:@"&"];
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
for (NSString *component in components) {
    NSArray *subcomponents = [component componentsSeparatedByString:@"="];
    [parameters setObject:[[subcomponents objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
                   forKey:[[subcomponents objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}

1
また、BadPirateによって指摘されたクラッシュの問題を超えて、(1)辞書のキー/値が逆になっていることにも注意してください。(2)キー/値はまだエンコードされているので、stringByDecodingURLFormat
dpjanes

それを行うための本当に素晴らしい方法
Burf2000 2013

9

以前のすべての投稿は、URLエンコードを正しく行いません。次の方法をお勧めします。

+(NSString*)concatenateQuery:(NSDictionary*)parameters {
    if([parameters count]==0) return nil;
    NSMutableString* query = [NSMutableString string];
    for(NSString* parameter in [parameters allKeys])
        [query appendFormat:@"&%@=%@",[parameter stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet],[[parameters objectForKey:parameter] stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]];
    return [[query substringFromIndex:1] copy];
}
+(NSDictionary*)splitQuery:(NSString*)query {
    if([query length]==0) return nil;
    NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
    for(NSString* parameter in [query componentsSeparatedByString:@"&"]) {
        NSRange range = [parameter rangeOfString:@"="];
        if(range.location!=NSNotFound)
            [parameters setObject:[[parameter substringFromIndex:range.location+range.length] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[[parameter substringToIndex:range.location] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
        else [parameters setObject:[[NSString alloc] init] forKey:[parameter stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    }
    return [parameters copy];
}

if(!query||[query length] == 0)冗長です。if([query length] == 0)その場合のように安全に確認できます。長さを呼び出そうとするとquery == nil、の値が返されます0
–JRG-開発者2013

これはvalueforkeyではなくobjectForKeyを使用するべきではありませんか?
slf 2013

素敵なコード。を使用NSUTF8StringEncodingsplitQueryてすべての文字セットをサポートすることをお勧めします:)また、[string stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]の新しいエンコード方法ですconcatenateQuery
William Denniss 2014年

不変のインスタンスを返すのは良い習慣だと思うので、最後の行を変更して[parameterscopy]を返す必要があるかもしれません。
グレンハウズ2014年

ありがとう。私はあなたのコメントに従ってコードを適応させました!
Kristian Kraljic 2015年

7

これがswiftの拡張機能です:

extension NSURL{
        func queryParams() -> [String:AnyObject] {
            var info : [String:AnyObject] = [String:AnyObject]()
            if let queryString = self.query{
                for parameter in queryString.componentsSeparatedByString("&"){
                    let parts = parameter.componentsSeparatedByString("=")
                    if parts.count > 1{
                        let key = (parts[0] as String).stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
                        let value = (parts[1] as String).stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
                        if key != nil && value != nil{
                            info[key!] = value
                        }
                    }
                }
            }
            return info
        }
    }

7

オナトのすでに非常にきれいな答えによると私はSwiftでNSURLの拡張機能を作成しました。ここで、次のようなクエリパラメータを取得できます。

たとえば、URLにはparam = some_valueのペアが含まれています

let queryItem = url.queryItemForKey("param")
let value = queryItem.value // would get String "someValue"

拡張機能は次のようになります。

extension NSURL {

  var allQueryItems: [NSURLQueryItem] {
      get {
          let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)!
          let allQueryItems = components.queryItems!
          return allQueryItems as [NSURLQueryItem]
      }
  }

  func queryItemForKey(key: String) -> NSURLQueryItem? {

      let predicate = NSPredicate(format: "name=%@", key)!
      return (allQueryItems as NSArray).filteredArrayUsingPredicate(predicate).first as? NSURLQueryItem

  }
}

これがベストアンサーです!なぜ彼らはAPIドキュメントをチェックしなかったのですか?
Jamin Zhang 2015

5

Bolts Frameworkを使用している場合は、次のものを使用できます。

NSDictionary *parameters = [BFURL URLWithURL:yourURL].inputQueryParameters;

インポートすることを忘れないでください:

#import <Bolts/BFURL.h>

プロジェクトにFacebookSDKが含まれている場合は、Boltsもあります。Facebookはこのフレームワークを依存関係として使用しています。


Swift with Bolts:BFURL(url: url)?.inputQueryParameters?["myKey"] as? String
JAL

4

URLを処理するための好ましい方法は現在NSURLComponentsです。特に、queryItemsパラメータのを返すプロパティNSArray

のパラメータが必要な場合はNSDictionary、次の方法があります。

+(NSDictionary<NSString *, NSString *>*)queryParamsFromURL:(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;
}

警告:URLには繰り返しパラメータを含めることができますが、ディクショナリには重複したパラメータの最後の値のみが含まれます。それが望ましくない場合は、queryItemsアレイを直接使用してください。


3

スウィフト2.1

一発ギャグ:

"p1=v1&p2=v2".componentsSeparatedByString("&").map {
    $0.componentsSeparatedByString("=") 
}.reduce([:]) {
    (var dict: [String:String], p) in
    dict[p[0]] = p[1]
    return dict
}

// ["p1": "v1", "p2": "v2"]

NSURLの拡張機能として使用されます:

extension NSURL {

    /**
     * URL query string as dictionary. Empty dictionary if query string is nil.
     */
    public var queryValues : [String:String] {
        get {
            if let q = self.query {
                return q.componentsSeparatedByString("&").map {
                    $0.componentsSeparatedByString("=") 
                }.reduce([:]) {
                    (var dict: [String:String], p) in
                    dict[p[0]] = p[1]
                    return dict
                }
            } else {
                return [:]
            }
        }
    }

}

例:

let url = NSURL(string: "http://example.com?p1=v1&p2=v2")!
let queryDict = url.queryValues

// ["p1": "v1", "p2": "v2"]

OS X10.10またはiOS8(またはそれ以降)を使用NSURLComponentsしている場合は、queryItemsプロパティを使用して、から辞書を作成することをお勧めします。NSURLQueryItems直接ます。

ここだNSURLComponentsベースのNSURL拡張ソリューションは:

extension NSURL {

    /// URL query string as a dictionary. Empty dictionary if query string is nil.
    public var queryValues : [String:String] {
        get {
            guard let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false) else {
                return [:]
            }

            guard let queryItems = components.queryItems else {
                return [:]
            }

            var result:[String:String] = [:]
            for q in queryItems {
                result[q.name] = q.value
            }
            return result
        }
    }

}

NSURL拡張機能の脚注は、Swiftでは、プロパティに既存の文字列プロパティと同じ名前を付けることが実際に可能であるということqueryです。試してみるまでわかりませんでしたが、Swiftのポリモーフィズムでは、戻り値の型のみが異なります。したがって、拡張NSURLプロパティがあれば、public var query: [String:String]それは機能します。少しおかしいと思うので、例ではこれを使用しませんでしたが、機能します...


2

私はMITの下で仕事をしている簡単なクラスを公開しました:

https://github.com/anegmawad/URLQueryToCocoa

これを使用すると、クエリに配列とオブジェクトを含めることができ、それらは収集されて接着されます

例えば

users[0][firstName]=Amin&users[0][lastName]=Negm&name=Devs&users[1][lastName]=Kienle&users[1][firstName]=Christian

となります:

@{
   name : @"Devs",
   users :
   @[
      @{
         firstName = @"Amin",
         lastName = @"Negm"
      },
      @{
         firstName = @"Christian",
         lastName = @"Kienle"
      }
   ]
 }

これは、のURLクエリ対応物と考えることができますNSJSONSerializer


素晴らしい仕事。これが唯一の正解のようです(配列とdictの値を考慮に入れるもの)。
アントントロパシュコ2016年

2

別のiOSアプリケーションからの受信データを処理するために使用しているようです。もしそうなら、これは私が同じ目的で使用するものです。

最初の呼び出し(外部アプリケーションなど):

UIApplication *application = [UIApplication sharedApplication];
NSURL *url = [NSURL URLWithString:@"myApp://action/1?parameter=2&secondparameter=3"];
if ([application canOpenURL:url]) {
    [application openURL:url];
    NSLog(@"myApp is installed");
} else {
    NSLog(@"myApp is not installed");
}

NSURLからQueryStringデータを抽出し、NSDictionaryとして保存する方法:

-(NSDictionary *) getNSDictionaryFromQueryString:(NSURL *)url {
   NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
   NSRange needle = [url.absoluteString rangeOfString:@"?" options:NSCaseInsensitiveSearch];
   NSString *data = nil;

   if(needle.location != NSNotFound) {
       NSUInteger start = needle.location + 1;
       NSUInteger end = [url.absoluteString length] - start;
       data = [url.absoluteString substringWithRange:NSMakeRange(start, end)];
   }

   for (NSString *param in [data componentsSeparatedByString:@"&"]) {
       NSArray *keyvalue = [param componentsSeparatedByString:@"="];
       if([keyvalue count] == 2){
           [result setObject:[keyvalue objectAtIndex:1] forKey:[keyvalue objectAtIndex:0]];
       }
   }

  return result;
}

使用法:

NSDictionary *result = [self getNSDictionaryFromQueryString:url];

0

このクラスは、URL解析に最適なソリューションです。

.hファイル

@interface URLParser : NSObject {
    NSArray *variables;
}

@property (nonatomic, retain) NSArray *variables;

- (id)initWithURLString:(NSString *)url;
- (NSString *)valueForVariable:(NSString *)varName;

@end

.mファイル

#import "URLParser.h"

@implementation URLParser
@synthesize variables;

- (id) initWithURLString:(NSString *)url{
    self = [super init];
    if (self != nil) {
        NSString *string = url;
        NSScanner *scanner = [NSScanner scannerWithString:string];
        [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];
        NSString *tempString;
        NSMutableArray *vars = [NSMutableArray new];
        [scanner scanUpToString:@"?" intoString:nil];       //ignore the beginning of the string and skip to the vars
        while ([scanner scanUpToString:@"&" intoString:&tempString]) {
            [vars addObject:[tempString copy]];
        }
        self.variables = vars;
    }
    return self;
}

- (NSString *)valueForVariable:(NSString *)varName {
    for (NSString *var in self.variables) {
        if ([var length] > [varName length]+1 && [[var substringWithRange:NSMakeRange(0, [varName length]+1)] isEqualToString:[varName stringByAppendingString:@"="]]) {
            NSString *varValue = [var substringFromIndex:[varName length]+1];
            return varValue;
        }
    }
    return nil;
}

@end

0

Hendrikはこの質問で拡張の良い例を書きましたが、objective-cライブラリメソッドを使用しないように書き直さなければなりませんでした。NSArray迅速に使用することは正しいアプローチではありません。

これが結果であり、すべて迅速でもう少し安全です。使用例は、Swift1.2ではコード行が少なくなります。

public extension NSURL {
    /*
    Set an array with all the query items
    */
    var allQueryItems: [NSURLQueryItem] {
        get {
            let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)!
            if let allQueryItems = components.queryItems {
                return allQueryItems as [NSURLQueryItem]
            } else {
                return []
            }
        }
    }

    /**
    Get a query item form the URL query

    :param: key The parameter to fetch from the URL query

    :returns: `NSURLQueryItem` the query item
    */
    public func queryItemForKey(key: String) -> NSURLQueryItem? {
        let filteredArray = filter(allQueryItems) { $0.name == key }

        if filteredArray.count > 0 {
            return filteredArray.first
        } else {
            return nil
        }
    }
}

使用法:

let queryItem = url.queryItemForKey("myItem")

または、より詳細な使用法:

if let url = NSURL(string: "http://www.domain.com/?myItem=something") {
    if let queryItem = url.queryItemForKey("myItem") {
        if let value = queryItem.value {
            println("The value of 'myItem' is: \(value)")
        }
    }
}

0

これを試して:

-(NSDictionary *)getUrlParameters:(NSString *)url{
    NSArray *justParamsArr = [url componentsSeparatedByString:@"?"];
    url = [justParamsArr lastObject];
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    for (NSString *param in [url componentsSeparatedByString:@"&"]) {
        NSArray *elts = [param componentsSeparatedByString:@"="];
        if([elts count] < 2) continue;
        [params setObject:[elts lastObject] forKey:[elts firstObject]];
    }
    return params;
}

0

かなりコンパクトなアプローチ:

    func stringParamsToDict(query: String) -> [String: String] {
        let params = query.components(separatedBy: "&").map {
            $0.components(separatedBy: "=")
        }.reduce(into: [String: String]()) { dict, pair in
            if pair.count == 2 {
                dict[pair[0]] = pair[1]
            }
        }
        return params
    }

-5

URLを使用してWebアプリから電話にデータを渡し、配列、数値、文字列などを渡したい場合の最も堅牢なソリューション...

オブジェクトをPHPでJSONエンコード

header("Location: myAppAction://".urlencode(json_encode($YOUROBJECT)));

そして、JSONはiOSで結果をデコードします

NSData *data = [[[request URL] host] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *packed = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

1
それはひどい考えです。URLには長さの制限があり、JSONをシリアル化する場合は簡単に超えることができます。
Michael Baldry 2013

その約2000文字
Michael Baldry 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.