読者です 読者をやめる 読者になる 読者になる

たぷつきません

おなかがでてきた。もうたぷついてるやん。

Apache CommonsのPropertyUtilみたいなもの

 サーバー側をJAXBで注釈付けたモデルをJSONオブジェクトで吐き出すサービス(RESTEasy+jettison-provider)にして、iPhone側をTouchJSONで受けるようにしたが、ちょっと参照の多いオブジェクトでは、TouchJSONのパース結果はDictionaryとArrayの階層の激しい塊になってしまう。キー値コーディングのkeyPathを使うにしても1つ1つ値を参照するのはちょっとどうだろうな感じ。
 なので、キー値コーディングとイントロスペクションを使って自動的にTouchJSONが得たDictionaryを、ユーザーの定義するモデルに変換するものを作成してみた。プロパティの型が曖昧な場合(TouchJSONの結果がNSArrayの要素がNSDictionaryで対象クラスもNSArray型のプロパティでしかなかったり、アブストラクトな型のプロパティだったりの意)のために、型補完するためのAPIを用意することで実現させた。型補完するAPIは、キー値コーディングライクなkeyPathに対応するClassを予め登録しておくというだけのもの。

@interface DictionaryToBeanUtil : NSObject {
    NSDictionary *_dictionary;
    NSMutableDictionary *_configureItemTypes;
}

@property (nonatomic, readonly) NSDictionary  *sourceDictionary;
@property (nonatomic, readonly) NSDictionary  *configureItemTypes;

+(void) assignTo:(NSObject*)root from:(NSDictionary*)dictionary;
+(void) assignTo:(NSObject*)root from:(NSDictionary*)dictionary types:(NSDictionary*)configureItemTypes;
-(id) initWithRootDictionary:(NSDictionary*)dictionary;
-(void) configureItemType:(Class)type keyPath:(NSString*)keyPath;
-(void) assignTo:(NSObject*)root;

@end

 ややこしかったのが、Objective-C Runtime Reference 2.0 にあるインストロスペクションを見ちゃっていて、Propertyという型が無かったり、iPhoneに中途半端にしか対応していなかったところ。で、実際は参照しているところが悪くて、こっちが正解だった。ちなみにドキュメントでは、id型のAttributeの説明しかないが(=T@)、NSString* や、MyClass* の場合は、T@"NSString" や T@"MyClass" という表現になる。これを以て、プロパティの型を特定してインスタンスを作成するようにした。ただ、アトリビュートを自前でパースするのが厄介。NSCoderのdecodeXXXも中で絶対パース処理をしているはずだから、ツールとして使えるメソッドが欲しかった。
 もう1つややこしいのが、jettison-providerが、ListをJSONにする際に、モデルの持つListプロパティの出力が単数か複数かで、配列表現になったりならなかったりするところだった。jettisonはどうもXMLJSONをしていて、Javaのモデルから判断していないと思われる。だとすれば、同じ名前の要素が連続していたら配列に、1つしか無ければ配列にしない...となるのは道理だ*1。これも変換対象クラスの同じキー(名前)がNSArrayになっていれば1つだろうと自動的に配列にして格納するようにした。
 そのうち公開したいけど忙しいので何時かやる。

*1:あれ?もしかしてschemagenとか使ってXMLスキーマを用意すれば良いのかな?