用于查询字符串的stringWithFormat变量参数列表

问题描述:

当参数数量可变时,用NSString建立查询的最智能方法是什么?用于查询字符串的stringWithFormat变量参数列表

比方说,我有一个Web服务,它返回一个由参数列表过滤的国家列表。例如:

NSString *continent = @"europe"; 
NSString *language = @"german"; 
NSString *timeZone = @"UTC+1"; 
// to be continued 

因此,我的查询字符串将是:

[NSString stringWithFormat:@"/getCountries?continent=%@&language=%@&timezone=%@", 
    continent, language, timeZone]; 

我的回答将包含

奥地利,德国和瑞士

,因为它们匹配三个参数。

现在我正在寻找最聪明的方式(例如不是意大利面代码if s)来构建此查询,当这些参数的0:n可能为零时。 (例如,当*timeZonenil时,该URL不应包含timezone=nil)结果应该是NSString。在一个NSDictionary

[NSString stringWithFormat:@"/getCountries?continent=%@&language=%@&timezone=%@", (continent ? continent : @""), (language ? language : @""), (timezone ? timezone : @"")]; 
+0

在零大陆的情况下,例如,什么是你最好:从查询中删除它或把它,但空的,就像这样:'大陆=语言...' –

+0

@LuizCarlosQuerinoFilho本来我正在寻找一种方法来完全删除零部分,但您的建议也应该在这种情况下工作。 – Chris

我会做一个对象,做这样一两件事,并且做得非常正确。

您必须注意的一个关键是确保您正确地转义所有名称和值。例如,如果您传递的值为“fred & wilma”,那么该URL中不应显示为“members = fred & wilma” - 您需要将&转义(如%26)。

当你装订时,你可以简单地逃避每个字符串,但是忘记这么做会很容易。代码中重复的任何内容都很容易被忽略。

这是我为此制作对象的主要原因。这个对象不仅会将线串起来,而且还会适当地逃避它们。

我假设你以后不需要查找键值。你只是想构建一个URL。

首先,对象应该私下拥有一个NSMutableString(不是公开的@property--私有@property或实例变量)。它应该在初始化时创建并在整个生命中保持它。

该对象的指定初始化程序应该是类似于initWithResourceURLString:。此方法发送字符串a mutableCopy消息并将结果分配给实例变量或私有属性。 init应该抛出异常(通过声明false最容易)。

该对象还应该有一个实例变量为单个unicharinitWithResourceURLString:应该将其设置为'?'

对象应该响应一条消息,如setQueryParameterWithName:toValue:。每个参数应该是一个字符串。如果任一字符串是nil,则该方法仅返回。

也就是说方法发送的每个字符串a stringByAddingPercentEscapesUsingEncoding: message,然后将可变的字符串的appendFormat:消息的格式如下:

%C%@=%@ 

与参数是在unichar实例变量,转义参数名的字符,和逃脱的价值。然后它将字符变量设置为&

该对象应通过返回可变字符串的副本(如果使用MRC,则为autoreleased)来响应诸如constructedURLString的消息。

你会再使用该对象,像这样:

builder = [[MyURLBuilder alloc] initWithResourceURLString:@"http://example.com/getCountries"]; //Add autorelease under MRC 
[builder setQueryParameterName:@"continent" toValue:continent]; 
[builder setQueryParameterName:@"language" toValue:language]; 
[builder setQueryParameterName:@"timeZone" toValue:timeZone]; 
NSURL *finishedURL = [NSURL URLWithString:[builder constructedURLString]]; 

优点:

  • 你究竟在确保转义一个地方是正确处理。所有其他代码不需要担心。
  • 您只有一个地方可以确保nil正确处理(忽略)。所有其他代码不需要担心。
  • 订单被保留。字典可能会改变参数的顺序。这应该不重要(服务器不应该在意),但如果它确实如此,那么您需要一个保留订单的解决方案。
  • 为这个对象编写单元测试会很容易。
  • 如果需要,您可以在多个方向上修改此对象。例如,您可以添加对重置构建的查询URL的支持,而无需修改或分别记住基本URL,从而使您可以将此对象用于多个查询。您还可以添加支持,以便将nil的空字符串替换为任何或仅用于某些参数。您对该课程所做的任何更改都会立即提供给您可能拥有的任何其他URL构建作业。
+0

很好的答案。照常! –

+0

谢谢你的详尽答案,我很高兴接受。没有其他办法了。 – Chris

您可以使用三元运算符,然后。然后走字典来建立你的参数字符串。这个实现将处理0到N个参数。您也可以通过参数化基本URL来概括它以用于API。

NSMutableString *paramString = [NSMutableString stringWithString:@"/getCountries"]; 
NSUInteger paramCount = 0; 
[params enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop){ 
    [paramString appendString:(paramCount == 0) ? @"?" : @"&"]; 
    NSString *newParam = [[NSString stringWithFormat:@"%@=%@", key, [params objectForKey:key]] stringByAddingPercentEscapesUsingEncoding:NSStringUTF8Encoding]; 
    [paramString appendString:newParam]; 
    paramCount++; 
}]; 
+0

右,三元将为空参数做。我希望有一些更优雅的东西。 – Chris

商店PARAMS不是个别NSString的的:

@XJones有一个良好的开端,但我认为这可能是更简单的实现:

NSDictionary *params = ...; 
NSMutableArray *pairs = [NSMutableArray array]; 
for (NSString *key in params) { 
    NSString *value = [params objectForKey:key]; 
    NSString *pair = [NSString stringWithFormat:@"%@=%@", key, value]; 
    [pairs addObject:pair]; 
} 
NSString *queryString = [pairs componentsJoinedByString:@"&"]; 
NSString *path = [NSString stringWithFormat:@"/getCountries?%@", queryString]; 

当然,你要正确的URL编码keyvalue

+1

小问题,我没有这样做,它需要走字典然后数组。我的解决方案只通过键/值对一次。 – XJones

+0

这很酷,我不知道'componentsJoinedByString'方法! –

+0

@luiz,你也会喜欢它在'NSString','componentsSeparatedByString:'中的对应部分。 – XJones

只需要把你的参数到一个NSDictionary,并建立基于这样的网址:

NSMutableDictionary * dict = [NSMutableDictionary dictionary]; 
if (continent) [dict setObject:continent forKey:@"continent"]; 
if (timeZone) [dict setObject:timeZone forKey:@"timeZone"]; 
NSMutableString * queryString = [NSMutableString stringWithString:@"/getCountries?"]; 

NSArray * allKeys = [dict allKeys]; 
for (NSUInteger i = 0; i < [allKeys count]; i++) { 
    NSString * key = [allKeys objectAtIndex:i]; 
    NSString * value = [dict objectForKey:key]; 
    NSString * escaped = [value stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]; 

    [queryString appendFormat:@"%@=%@", key, escaped]; 
    if (i + 1 < [allKeys count]) { 
     [queryString appendString:@"&"]; 
    } 
}