2011年3月3日

ODataObjCで日本語パラメータを使う方法

OData Client for Objective-C(http://odataobjc.codeplex.com/)を触っていて気づいたことが、Azure Tableストレージを対象に日本語をパラメータとして検索するフィルタクエリを実行すると、Cocoaのライブラリ側で不正なURLという例外が発生してしまう。

なぜそんなことになっているかソースコードを見ていたら、ライブラリ内部ではフィルタクエリのURLエンコードを行っていないことがわかった。また、そのソースの近くにURLエンコードを試みたらしくコメントアウトされたコードがあるものの、それを外しても動かない。

原因をよくよく考えてみると、Azure Tableストレージに対するエンティティのクエリ構文では、全体をURLエンコードするのではなく個々のフィルタパラメータをURLエンコードしなくてはならないことがわかった。

例えば、

$filter=(PartitionKey eq 'あいうえお')

こんなクエリを投げたいときは

$filter=(PartitionKey%20eq%20%27%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A%27)

ではなく、

$filter=(PartitionKey%20eq%20'%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A')

のようにしなければならない(%20は+でもOK)。

しかし、このODataObjCではこの$filter=の後の文字列を直接メソッドのパラメータとして受け取るように作られているため、ライブラリ内部で対応するのは難しい感じです。(だから諦めたのかな。。。)

しかしこのままじゃ使えないため、$filter=の後の文字列を生成する簡単なクエリビルダを作成し、その中でURLエンコードするようにして対処してみました。というわけで作成したクエリビルダのソースを置いておきます。(URLエンコードの処理はODataObjCライブラリのUtilityクラスを利用してます)

Queries.h

#import <Foundation/Foundation.h>
#import "Utility.h"


@interface Queries : NSObject {
@private
NSString *filterString;
}

@property (nonatomic, retain) NSString *filterString;

- (id) initWithString:(NSString *)aFilterString;

+ (Queries *) withEmpty;
+ (Queries *) with:(NSString *)name eq:(NSString *)value;
+ (Queries *) with:(NSString *)name gt:(NSString *)value;
+ (Queries *) with:(NSString *)name ge:(NSString *)value;
+ (Queries *) with:(NSString *)name lt:(NSString *)value;
+ (Queries *) with:(NSString *)name le:(NSString *)value;
+ (Queries *) with:(NSString *)name ne:(NSString *)value;

- (Queries *) and:(Queries *)aQueries;
- (Queries *) or:(Queries *)aQueries;
- (Queries *) not;

@end

 

Queries.m

#import "Queries.h"


@implementation Queries

@synthesize filterString;

- (id) initWithString:(NSString *)aFilterString {
self.filterString = aFilterString;

return self;
}

+ (Queries *) withEmpty {
return [[[Queries alloc] initWithString:@""] autorelease];
}

+ (Queries *) with:(NSString *)name eq:(NSString *)value {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(%@ eq '%@')", name, [Utility URLEncode:value]]] autorelease];
}

+ (Queries *) with:(NSString *)name gt:(NSString *)value {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(%@ gt '%@')", name, [Utility URLEncode:value]]] autorelease];
}

+ (Queries *) with:(NSString *)name ge:(NSString *)value {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(%@ ge '%@')", name, [Utility URLEncode:value]]] autorelease];
}

+ (Queries *) with:(NSString *)name lt:(NSString *)value {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(%@ lt '%@')", name, [Utility URLEncode:value]]] autorelease];
}

+ (Queries *) with:(NSString *)name le:(NSString *)value {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(%@ le '%@')", name, [Utility URLEncode:value]]] autorelease];
}

+ (Queries *) with:(NSString *)name ne:(NSString *)value {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(%@ ne '%@')", name, [Utility URLEncode:value]]] autorelease];
}

- (Queries *) and:(Queries *)aQueries {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(%@ and %@)", self.filterString, aQueries.filterString]] autorelease];
}

- (Queries *) or:(Queries *)aQueries {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(%@ or %@)", self.filterString, aQueries.filterString]] autorelease];
}

- (Queries *) not {
return [[[Queries alloc] initWithString:[NSString stringWithFormat:@"(not %@)", self.filterString]] autorelease];
}

- (void) dealloc {
[self.filterString release];

[super dealloc];
}

@end

 

このクエリビルダを使うと上記のクエリは、

[Queries with:@"PartitionKey" eq:@"あいうえお"].filterString;

と書ける。

0 件のコメント:

コメントを投稿