KissXMLを使ってみる

-(NSArray *)friendsPictures{
  NSURL *url = [NSURL URLWithString: @"http://movapic.com/feed/user/fn7"];
  NSData *data = [NSData dataWithContentsOfURL: url];

  NSError *error = nil;
  DDXMLDocument *doc = [[DDXMLDocument alloc] initWithData:data options:0 error:&error];
  if(error != nil){
    NSLog(@"ERROR: %@",[error localizedDescription]);
    return nil;
  }

  DDXMLElement *root = [doc rootElement];
  DDXMLElement *channel = [[doc nodesForXPath:@"//channel" error: nil] objectAtIndex:0];
  NSArray *title = [channel  nodesForXPath: @"item/media:group/media:title" error:nil];
  NSArray *pubDate = [channel nodesForXPath: @"item/pubDate" error:nil];
  NSArray *imageURL = [channel nodesForXPath: @"item/media:group/media:content" error:nil];

  NSMutableArray *items = [NSMutableArray array];
  for(int i=0;i<[title count];i++){
    [items addObject: [NSDictionary dictionaryWithObjectsAndKeys:
      [[title objectAtIndex:i] stringValue],@"title",
      [[pubDate objectAtIndex:i] stringValue],@"pubDate",
      [[[imageURL objectAtIdex:i] attributeForName:@"url"] stringValue],@"imageURL",
      nil]];
  }
}

NSErrorは使う前にnilで初期化しておくのが良いそうだ。
しかし面倒なので、XPath指定のとこはエラーを無視している。

DDXMLDocumentの生成時に指定するoptionsの意味がまだよくわかっていない。

for文のところではtitle,pubDate,imageURLのそれぞれの長さが違うと当然エラーが発生する。まぁ、RSSの場合、きっちり入っているはずなのでよしとする。

以下、実行結果。

2010-02-02 23:24:25.716 Movapic[43825:207] (
        {
        image = "http://image.movapic.com/pic/m_201001311205424b64f38630fa9.jpeg";
        pubDate = "Sun, 31 Jan 2010 12:05:53 +0900";
        title = "fn7 - \U304a\U54c1\U66f8\U304d\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001311157104b64f186d75e5.jpeg";
        pubDate = "Sun, 31 Jan 2010 11:57:21 +0900";
        title = "fn7 - \U6625\U9e7f\U3067\U5229\U304d\U9152w\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001311118044b64e85c0307f.jpeg";
        pubDate = "Sun, 31 Jan 2010 11:18:13 +0900";
        title = "fn7 - \U5229\U304d\U9152\U4e2d\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001240201184b5b2b5e96b30.jpeg";
        pubDate = "Sun, 24 Jan 2010 02:01:29 +0900";
        title = "fn7 - \U70cf\U9aa8\U9d8f\U306e\U305f\U307e\U3054\U304b\U3051\U3054\U306f\U3093(\U96fb\U6c60\U304d\U308c\U3066\U305f\U306e\U3067\U3044\U307e\U6295\U7a3f)\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001232027234b5add1b6a333.jpeg";
        pubDate = "Sat, 23 Jan 2010 20:27:34 +0900";
        title = "fn7 - \U713c\U304d\U304a\U306b\U304e\U308a\U306e\U304a\U8336\U6f2c\U3051\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001232006344b5ad83aa78e2.jpeg";
        pubDate = "Sat, 23 Jan 2010 20:06:45 +0900";
        title = "fn7 - \U5e73\U8c9d\U713c\U971c\U9020\U308a \U30b5\U30e9\U30c0\U4ed5\U7acb\U3066 \U3068\U7279\U5225\U7d14\U7c73\U9152 \U306e\U71d7\U9152\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001231947044b5ad3a892ac6.jpeg";
        pubDate = "Sat, 23 Jan 2010 19:47:14 +0900";
        title = "fn7 - \U767d\U9b5a\U3068\U6625\U91ce\U83dc\U306e\U5929\U5a66\U7f85\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001231927024b5acef6ce6ba.jpeg";
        pubDate = "Sat, 23 Jan 2010 19:27:16 +0900";
        title = "fn7 - \U9c06\U67da\U5eb5\U713c\U304d\U9152\U7c95\U30bd\U30fc\U30b9\U304b\U3051\U3068\U306b\U3054\U308a\U9152\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001231908444b5acaacbe76a.jpeg";
        pubDate = "Sat, 23 Jan 2010 19:08:54 +0900";
        title = "fn7 - \U304a\U3070\U3093\U3056\U3044\U3068\U8f9b\U53e3\U672c\U91b8\U9020\n\n\n";
    },
        {
        image = "http://image.movapic.com/pic/m_201001231856284b5ac7cceeb93.jpeg";
        pubDate = "Sat, 23 Jan 2010 18:56:41 +0900";
        title = "fn7 - \U523a\U8eab\U3068\U541f\U91b8\U751f\U3057\U307c\U308a\U305f\U3066\n\n\n";
    }
)

ただし携帯百景RSSの中でには実体参照されている文字があって、それがどうもXMLのパースエラーになっているようだ。chromeRSSを表示させてみたときにもエラーが表示された。


回避策するにはDDXMLDocumentを作る前に実体参照を変更しちゃうようにすればうまくいく。
この程度の対策でエラーは抑制できたけど、根本解決になっているかはまだわからない。

NSData *data = [NSData dataWithContentsOfURL:url]; 
NSString *xml = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // NSDataをNSStringにする。
xml = [xml decodeSpecialChars]; // 実体参照を文字列置換する
NSLog(@"xml: %@",xml);
NSError *error = nil;
DDXMLDocument *doc = [[DDXMLDocument alloc] initWithXMLString:xml options:0 error:&error];
[xml release];

実体参照の変更

カテゴリを使ってみた。自分をコピーして変換した結果を返すメソッドをNSStringクラスに追加した。

@interface NSString (DecodeSpecialChars)

-(NSString *)decodeSpecialChars;

@end

@implementation NSString (DecodeSpecialChars)
-(NSString *)decodeSpecialChars{
	NSDictionary *map = [NSDictionary dictionaryWithObjectsAndKeys:
							 @"&#402;",@"&fnof;",
							 @"Α",@"&Alpha;",
							 @"Β",@"&Beta;",
							 @"Γ",@"&Gamma;",
							 @"Δ",@"&Delta;",
							 @"Ε",@"&Epsilon;",
							 @"Ζ",@"&Zeta;",
							 @"Η",@"&Eta;",
							 @"Θ",@"&Theta;",
							 @"Ι",@"&Iota;",
							 @"Κ",@"&Kappa;",
							 @"Λ",@"&Lambda;",
							 @"Μ",@"&Mu;",
							 @"Ν",@"&Nu;",
							 @"Ξ",@"&Xi;",
							 @"Ο",@"&Omicron;",
							 @"Π",@"&Pi;",
							 @"Ρ",@"&Rho;",
							 @"Σ",@"&Sigma;",
							 @"Τ",@"&Tau;",
							 @"Υ",@"&Upsilon;",
							 @"Φ",@"&Phi;",
							 @"Χ",@"&Chi;",
							 @"Ψ",@"&Psi;",
							 @"Ω",@"&Omega;",
							 @"α",@"&alpha;",
							 @"β",@"&beta;",
							 @"γ",@"&gamma;",
							 @"δ",@"&delta;",
							 @"ε",@"&epsilon;",
							 @"ζ",@"&zeta;",
							 @"η",@"&eta;",
							 @"θ",@"&theta;",
							 @"ι",@"&iota;",
							 @"κ",@"&kappa;",
							 @"λ",@"&lambda;",
							 @"μ",@"&mu;",
							 @"ν",@"&nu;",
							 @"ξ",@"&xi;",
							 @"ο",@"&omicron;",
							 @"π",@"&pi;",
							 @"ρ",@"&rho;",
							 @"ς",@"&sigmaf;",
							 @"σ",@"&sigma;",
							 @"τ",@"&tau;",
							 @"υ",@"&upsilon;",
							 @"φ",@"&phi;",
							 @"χ",@"&chi;",
							 @"ψ",@"&psi;",
							 @"ω",@"&omega;",
							 @"&#977;",@"&thetasym;",
							 @"&#978;",@"&upsih;",
							 @"&#982;",@"&piv;",
							 @"&#8226;",@"&bull;",
							 @"…",@"&hellip;",
							 @"′",@"&prime;",
							 @"″",@"&Prime;",
							 @"~",@"&oline;",
							 @"&#8260;",@"&frasl;",
							 @"&#8472;",@"&weierp;",
							 @"&#8465;",@"&image;",
							 @"&#8476;",@"&real;",
							 @"™",@"&trade;",
							 @"&#8501;",@"&alefsym;",
							 @"←",@"&larr;",
							 @"→",@"&rarr;",
							 @"↓",@"&darr;",
							 @"&#8596;",@"&harr;",
							 @"&#8629;",@"&crarr;",
							 @"&#8656;",@"&lArr;",
							 @"&#8657;",@"&uArr;",
							 @"⇒",@"&rArr;",
							 @"&#8659;",@"&dArr;",
							 @"⇔",@"&hArr;",
							 @"∀",@"&forall;",
							 @"∂",@"&part;",
							 @"∃",@"&exist;",
							 @"&#8709;",@"&empty;",
							 @"∇",@"&nabla;",
							 @"∈",@"&isin;",
							 @"&#8713;",@"&notin;",
							 @"∋",@"&ni;",
							 @"&#8719;",@"&prod;",
							 @"??",@"&sum;",
							 @"−",@"&minus;",
							 @"&#8727;",@"&lowast;",
							 @"√",@"&radic;",
							 @"∝",@"&prop;",
							 @"∞",@"&infin;",
							 @"∠",@"&ang;",
							 @"∧",@"&and;",
							 @"∨",@"&or;",
							 @"∩",@"&cap;",
							 @"∪",@"&cup;",
							 @"∫",@"&int;",
							 @"∴",@"&there4;",
							 @"&#8764;",@"&sim;",
							 @"&#8773;",@"&cong;",
							 @"&#8776;",@"&asymp;",
							 @"≠",@"&ne;",
							 @"≡",@"&equiv;",
							 @"&#8804;",@"&le;",
							 @"&#8805;",@"&ge;",
							 @"⊂",@"&sub;",
							 @"⊃",@"&sup;",
							 @"&#8836;",@"&nsub;",
							 @"⊆",@"&sube;",
							 @"⊇",@"&supe;",
							 @"&#8853;",@"&oplus;",
							 @"&#8855;",@"&otimes;",
							 @"⊥",@"&perp;",
							 @"&#8901;",@"&sdot;",
							 @"&#8968;",@"&lceil;",
							 @"&#8969;",@"&rceil;",
							 @"&#8970;",@"&lfloor;",
							 @"&#8971;",@"&rfloor;",
							 @"〈",@"&lang;",
							 @"〉",@"&rang;",
							 @"&#9674;",@"&loz;",
							 @"&#9824;",@"&spades;",
							 @"&#9827;",@"&clubs;",
							 @"&#9829;",@"&hearts;",
							 @"&#9830;",@"&diams;",											 
							 nil];
	NSString *result = [self copy];
	for(NSString *k in [map keyEnumerator]){
		result = [result stringByReplacingOccurrencesOfString:k withString:[map objectForKey:k]];
	}
	return result;
}
@end

はてな記法の中なので一部の文字が数値参照になってしまっているが、実際にどの文字がどうなっているのかはWikipedia等を参照して頂きたい。


やっぱ、XPath使えると大分使い安さが違う。XPath万歳!

[追記]
ここに記載した変換では不十分で、他の文字についても変換が必要なことがわかっています。Wikipediaやその他のページを参考にマッピングを追加されることをお勧めします。