TwitterをObjective-Cから使う8(MGTwitterLibXMLParserの修正)
libxml2を使うときに、MGTwitterEngineを使ってうまくいかなかったところを、メモしておきます。
users lookupなどのAPIでユーザ情報を取得するときに、xmlだとこんな感じで返ってくるのですが。
6253282 Twitter API twitterapi San Francisco, CA ・ ・ ・ ・Mon Jun 21 13:54:52 +0000 2010 16695527388 High Error Rate on Twitter.com - We’re seeing periodic high rates of errors on Twitter.com. http://t.co/vnLr0qV ・ ・ ・・ ・ ・
usersの中にuserが複数はいる構造です。そしてuserの中にstatusが入る階層構造となっているのですが、libxml2だと、そこのパースがうまくいきません。
MGTwitterLibXMLParser.mの_userDictionaryForNodeWithNameメソッドをみると、下記のようになっています。
上記のxmlをパースすると、階層構造に対応されていないため、user直下のidとstatus配下のidがぶつかって上書きされてしまいます。
- (NSDictionary *)_userDictionaryForNodeWithName:(const xmlChar *)parentNodeName { ・ ・ ・ while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(parentNodeName, name))) { if (nodeType == XML_READER_TYPE_ELEMENT) { if (xmlStrEqual(name, BAD_CAST "id") || xmlStrEqual(name, BAD_CAST "followers_count") || xmlStrEqual(name, BAD_CAST "friends_count") || xmlStrEqual(name, BAD_CAST "favourites_count") || xmlStrEqual(name, BAD_CAST "statuses_count")) { // process element as an integer NSNumber *number = [self _nodeValueAsInt]; if (number) { [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]]; } } else if (xmlStrEqual(name, BAD_CAST "protected")) { // process element as a boolean NSNumber *number = [self _nodeValueAsBool]; if (number) { [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]]; } } else { // process element as a string NSString *s = [self _nodeValueAsString]; if (s) { [dictionary setObject:s forKey:[NSString stringWithUTF8String:(const char *)name]]; } } } ・ ・ ・ return dictionary; }
そこで、"status"ノードの場合は、_statusDictionaryForNodeWithNameで、もうちょっとばらしてあげるように修正が必要です。
また、ついでにprotected以外にも、geo_enabledなど数値で返ってくるものがありますので、そこら辺もついでに修正します。
- (NSDictionary *)_userDictionaryForNodeWithName:(const xmlChar *)parentNodeName { if (xmlTextReaderIsEmptyElement(_reader)) return nil; NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; int readerResult = xmlTextReaderRead(_reader); if (readerResult != 1) return nil; int nodeType = xmlTextReaderNodeType(_reader); const xmlChar *name = xmlTextReaderConstName(_reader); while (! (nodeType == XML_READER_TYPE_END_ELEMENT && xmlStrEqual(parentNodeName, name))) { if (nodeType == XML_READER_TYPE_ELEMENT) { if (xmlStrEqual(name, BAD_CAST "status")) { [dictionary setObject:[self _statusDictionaryForNodeWithName:name] forKey:@"status"]; } else if (xmlStrEqual(name, BAD_CAST "id") || xmlStrEqual(name, BAD_CAST "followers_count") || xmlStrEqual(name, BAD_CAST "friends_count") || xmlStrEqual(name, BAD_CAST "favourites_count") || xmlStrEqual(name, BAD_CAST "statuses_count")) { // process element as an integer NSNumber *number = [self _nodeValueAsInt]; if (number) { [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]]; } } else if (xmlStrEqual(name, BAD_CAST "protected") || xmlStrEqual(name, BAD_CAST "notifications") || xmlStrEqual(name, BAD_CAST "geo_enabled") || xmlStrEqual(name, BAD_CAST "verified") || xmlStrEqual(name, BAD_CAST "profile_background_tile") || xmlStrEqual(name, BAD_CAST "truncated") || xmlStrEqual(name, BAD_CAST "favorited")) { // process element as a boolean NSNumber *number = [self _nodeValueAsBool]; if (number) { [dictionary setObject:number forKey:[NSString stringWithUTF8String:(const char *)name]]; } } else { // process element as a string NSString *s = [self _nodeValueAsString]; if (s) { [dictionary setObject:s forKey:[NSString stringWithUTF8String:(const char *)name]]; } } } // advance reader readerResult = xmlTextReaderRead(_reader); if (readerResult != 1) break; nodeType = xmlTextReaderNodeType(_reader); name = xmlTextReaderConstName(_reader); } return dictionary; }
これで、ちゃんとパースされるはずです。