【问题标题】:Parse NSURL query property解析 NSURL 查询属性
【发布时间】:2011-04-29 05:14:45
【问题描述】:

我有一个类似myApp://action/1?parameter=2&secondparameter=3 的网址

通过属性查询,我得到了 URL 的以下部分内容

parameter=2&secondparameter=3

有什么方法可以轻松地将其放入NSDictionaryArray

非常感谢

【问题讨论】:

    标签: iphone objective-c nsurl


    【解决方案1】:

    试试这个;)!

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

    【讨论】:

    • 还要注意——除了 BadPirate 指出的崩溃问题——(1)字典键/值被颠倒了; (2) 键/值仍然是编码的,所以你想做 stringByDecodingURLFormat
    • 非常好的方法
    【解决方案2】:

    类似的东西:

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

    注意:这是示例代码。并非所有错误情况都得到管理。

    【讨论】:

    • 如果查询格式错误,此代码将崩溃。请参阅下面的安全解决方案。
    • 好的,您只需添加对数组大小的检查!这段代码只是一个开始(一如既往)。如果你想符合所有的 URL,你还有更多的工作来处理转义字符串,片段(最后的#部分),...
    • 为您进行编辑。只有投了反对票才能引起您对答案问题的关注。编辑完成后,我可以将其恢复。
    • 除了没有特殊字符的最简单的大小写值之外,此代码甚至不接近正确。
    • 你应该使用 last & first 对象来代替:[params setObject:[elts lastObject] forKey:[elts firstObject]]; 它更安全
    【解决方案3】:

    我有理由为此行为编写一些可能派上用场的扩展。首先是标题:

    #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
    

    【讨论】:

    • 两个问题:1)你必须在将键和值保存到字典之前对它们进行URL解码,2)根据URL的规则,你可以指定相同的键多次。 IOW,它应该是映射到值数组的键,而不是单个值。
    • 好的,您只需添加对数组大小的检查(这需要对我的答案投反对票吗?)!这段代码只是一个开始(一如既往)。如果你想符合所有的 URL,你还有更多的工作来处理转义字符串,片段(最后的#部分),...
    • 戴夫 - 好点。不是我的需求所必需的,但由于我试图做出一个适合所有访问这个常见问题的人的答案,所以我已经包含了提到的功能...... Benoit - 只是用你的答案来提醒你注意这个问题,我提交了一个编辑。可能不是使用 down 来引起对其他好答案中小问题的注意的最佳形式,我将来会避免这样做.. 在编辑完成之前无法更改否决票:/
    • 在 stringByDecodingURLFormat 中,'result' 的类型应该是 NSString* 。
    • 刚刚注意到另一个问题。键和值混淆了,键应该是objectAtIndex:0,值应该是objectAtIndex:1。
    【解决方案4】:

    如果您使用 URL 将数据从网络应用程序传递到手机,并且您想要传递数组、数字、字符串等,这是最可靠的解决方案...

    JSON 用 PHP 编码你的对象

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

    然后在 iOS 中用 JSON 解码结果

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

    【讨论】:

    • 那是个糟糕的主意。 URL 有一个长度限制,如果您将 JSON 序列化到其中,则很容易超出该限制。
    • 大约 2000 个字符
    【解决方案5】:

    之前的所有帖子都没有正确地进行 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的值。
    • 不应该使用objectForKey而不是valueforkey吗?
    • 不错的代码。我建议在splitQuery 中使用NSUTF8StringEncoding 来支持所有字符集:) 另外,[string stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]concatenateQuery 的一种较新的编码方式。
    • 我认为返回一个不可变的实例是个好习惯,所以也许你应该把最后一行改成返回 [parameters copy];
    • 谢谢。我已经根据你的 cmets 修改了代码!
    【解决方案6】:

    这个类是一个很好的 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
    

    【讨论】:

      【解决方案7】:

      我在 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"
            }
         ]
       }
      

      您可以将其视为NSJSONSerializer 的 URL 查询对应项。

      【讨论】:

      • 出色的工作。这似乎是唯一正确的答案(将数组和字典值考虑在内的答案)。
      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        您可以在URLComponents 中使用queryItems

        当你得到这个属性的值时,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"])
        

        【讨论】:

        • 从 iOS7 开始的查询。从 iOS8 开始的 queryItems。
        • 感谢您的回答。除此之外,我发现下面的文章对了解更多关于 NSURLComponents 很有帮助:nshipster.com/nsurl
        • 很好的答案。谢谢!
        • 奇怪的是,如果 URL 中有 # 字符,NSURLComponents 就不起作用。喜欢http://example.com/abc#abc?param1=value1&amp;param2=param2 queryItems 将为零
        • 那是因为片段(#abc)属于最后。
        【解决方案10】:

        对于那些使用 Bolts Framework 的人,您可以使用:

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

        记得导入:

        #import <Bolts/BFURL.h>
        

        如果您的项目中碰巧有 Facebook SDK,那么您也有 Bolts。 Facebook 正在将此框架用作依赖项。

        【讨论】:

        • 在 Swift 中使用螺栓:BFURL(url: url)?.inputQueryParameters?["myKey"] as? String
        【解决方案11】:

        根据已经very clean answer of Onato,我在 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文档?
        【解决方案12】:

        Hendrik 在这个问题中写了一个很好的扩展示例,但是我不得不重新编写它以不使用任何objective-c 库方法。在 swift 中使用 NSArray 不是正确的方法。

        这就是结果,一切都很快,而且更安全。 Swift 1.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)")
                }
            }
        }
        

        【讨论】:

          【解决方案13】:

          斯威夫特 2.1

          Oneliner:

          "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 X 10.10 或 iOS 8(或更高版本),最好使用 NSURLComponentsqueryItems 属性并直接从 NSURLQueryItems 创建字典。

          这是一个基于NSURLComponentsNSURL 扩展解决方案:

          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] 它可以工作。我没有在示例中使用它,因为我觉得它有点疯狂,但它确实有效......

          【讨论】:

            【解决方案14】:

            看起来您正在使用它来处理来自另一个 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];
            

            【讨论】:

              【解决方案15】:

              现在处理 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 数组。

              【讨论】:

                【解决方案16】:

                试试这个:

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

                【讨论】:

                  【解决方案17】:

                  相当紧凑的方法:

                      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
                      }
                  

                  【讨论】:

                    猜你喜欢
                    • 2010-12-30
                    • 2018-03-13
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-12-15
                    • 1970-01-01
                    • 2014-12-27
                    • 1970-01-01
                    相关资源
                    最近更新 更多