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
	<a href="http://www.tumblr.com/" rel="nofollow">Tumblr</a>
	 ・
	 ・
	 ・
	
  


	 ・
	 ・
	 ・


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;
}

これで、ちゃんとパースされるはずです。