2011-11-24 35 views
2

當參數數量可變時,用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

回答

5

我會做一個對象,做這樣一兩件事,並且做得非常正確。

您必須注意的一個關鍵是確保您正確地轉義所有名稱和值。例如,如果您傳遞的值爲「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

2

您可以使用三元運算符,然後。然後走字典來建立你的參數字符串。這個實現將處理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

2

商店PARAMS不是個別NSString的的:

1

@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

1

只需要把你的參數到一個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:@"&"]; 
    } 
}