C言語のlibxml2を使ってwebAPIのXMLを解析

apacheモジュール内でwebAPIを呼んでXMLを解析する必要があるため、C/C++で利用できるXMLパーサを調べてみた。

Cだと

  • expat
  • libxml2

C++だと

  • tinyXml
  • Xerces

などなど他にもさまざまなライブラリがあるようだが今回はlibxml2を使ってパースしてみる事とした。

XMLの知識に乏しいのと
C言語が苦手なのと
ライブラリのAPIが多すぎて
などが重なってかなり苦戦しました。

でもXpathが使えるのは便利だなあと思った。
こちらの方のコードを参考に作らせていただきました。

yahoo!掲示板のRSSのitem要素を出力するだけのプログラム

#include <stdio.h>
#include <libxml/xmlreader.h>
#include <libxml/xpath.h>

enum Status {
    Success = 0,
    Fail    = -1,
};

const char* REQUEST_API    = "http://messages.yahoo.co.jp/bbs?action=4&board=552019556&sid=552019556&tid=kcc08bc0obafb3u";
const char* XPATH_ITEM     = "/rss//item/*/text()";        // item以下全てのXPATH
const char* XPATH_TITLE    = "/rss//item/title/text()";    // titleのXPATH
const char* XPATH_PUB_DATE = "/rss//item/pubDate/text()";  // pubDateのXPATH
    
int main(void) {
    xmlTextReaderPtr reader =  xmlNewTextReaderFilename(REQUEST_API);
    if (!reader) return Fail;

    xmlTextReaderRead(reader);
    xmlTextReaderExpand(reader);
    xmlDocPtr doc = xmlTextReaderCurrentDoc(reader);
    if (!doc) return Fail;

    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (!ctx) return Fail;
    
    xmlXPathObjectPtr xpobj = xmlXPathEvalExpression((xmlChar *)XPATH_ITEM, ctx);
    if (!xpobj) return Fail;

    xmlNodeSetPtr nodes = xpobj->nodesetval;
    int size = (nodes) ? nodes->nodeNr : 0;
    for (int i = 0; i < size; ++i) {
        if (!xmlXPathNodeSetIsEmpty(nodes)) {
            xmlNodePtr node = xmlXPathNodeSetItem(nodes, i);
            if (node->content) {
                printf("%s => %s\n", node->parent->name, node->content);
            } else {
                printf("invalid node\n");
                return Fail;
            }
        }
    }

    xmlXPathFreeObject(xpobj);
    xmlXPathFreeContext(ctx);
    xmlFreeDoc(doc);
    xmlFreeTextReader(reader);
    xmlCleanupParser();
    return Success;
}

追記(20090322)

xmlDocPtrを取得するために以下のようにやっていましたが

xmlTextReaderPtr reader =  xmlNewTextReaderFilename(REQUEST_API);
if (!reader) return Fail;

xmlTextReaderRead(reader);
xmlTextReaderExpand(reader);
xmlDocPtr doc = xmlTextReaderCurrentDoc(reader);

本家サンプルを見るとこれだけで取れるみたい。

xmlDocPtr doc = xmlParseFile(REQUEST_API);

総括

phpのXML系extensionやperlのXSのXML::LibXMLのソースコードを読んだほうがlibxml2を理解できるのかもしれない。
こういう処理はLLに比べてどれだけ早いのかベンチとってみたい。