Big Sky :: ヘッダファイルだけでC++から使えるJSONパーサ「picojson」が凄い! で紹介されていたコードをベースにして、コマンドライン引数を検索キーワードとするTwitter検索を作ってみた。

ソースコード: twisearch.cpp


#include <curl/curl.h> // /usr/include/curl/curl.h
#include "picojson.h"
 
typedef struct {
  char* data;   // response data from server
  size_t size;  // response size of data
} MEMFILE;
 
MEMFILE*
memfopen() {
  MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE));
  mf->data = NULL;
  mf->size = 0;
  return mf;
}
 
void
memfclose(MEMFILE* mf) {
  if (mf->data) free(mf->data);
  free(mf);
}
 
size_t
memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) {
  MEMFILE* mf = (MEMFILE*) stream;
  int block = size * nmemb;
  if (!mf->data)
    mf->data = (char*) malloc(block);
  else
    mf->data = (char*) realloc(mf->data, mf->size + block);
  if (mf->data) {
    memcpy(mf->data + mf->size, ptr, block);
    mf->size += block;
  }
  return block;
}
 
char*
memfstrdup(MEMFILE* mf) {
  char* buf = (char*)malloc(mf->size + 1);
  memcpy(buf, mf->data, mf->size);
  buf[mf->size] = 0;
  return buf;
}
 
using namespace std;
using namespace picojson;
 
int
main(int argc, char* argv[]) {
 
  string q = "";
  if(argc > 1){
    for(int i=1; i<argc; i++){
      if(i != 1){
        q += "%20";
      }
      q += argv[i];
    }
  }else{
    q = "twitter"; // default query
  }
  cout << "q=[" << q << "]" << endl;
  string url = "http://search.twitter.com/search.json?q=" + q;
  
  char error[256];
  
  MEMFILE* mf = memfopen();
  CURL* curl = curl_easy_init();
  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
  if (curl_easy_perform(curl) != CURLE_OK) {
    cerr << error << endl;
  } else {
    value v;
    string err;
    parse(v, mf->data, mf->data + mf->size, &err);
    if (err.empty()) {
 
      // picojson::array is std::vector
      // typedef std::vector<value> array;
      
      // picojson::object is std::map
      // typedef std::map<std::string, value> object;
      
      // root: object
      object root = v.get<object>();
      
      // query: string
      value query = root["query"];
      cout << "query=[" << query.to_str() << "]" << endl;
      cout << endl;
      
      // results: array
      value results = root["results"];
      array tweets = results.get<array>();
      cout << "number of tweets=[" << tweets.size() << "]" << endl;
      cout << endl;
      array::iterator it;
      for (it = tweets.begin(); it != tweets.end(); it++) {
        // print tweet
        object obj = it->get<object>();
        string name = obj["from_user"].to_str();
        string text = obj["text"].to_str();
        cout << "[" + name << "] " <<  text << endl;
        cout << endl;
      }
    } else {
      cerr << err << endl;
    }
  }
  curl_easy_cleanup(curl);
  memfclose(mf);

  return 0;
}

コンパイル。


$ g++ -o twisearch -lcurl ./twisearch.cpp

実行結果。
キーワードはコマンドライン引数で複数指定できる (でも%エンコードしてないので日本語ダメ)


$ ./twisearch java ruby
q=[java%20ruby]
query=[java+ruby]
 
number of tweets=[15]
 
[nilab] Java / Ruby / (・∀・)イイ!!
 
[nilab] Java, Ruby, Scheme
 
[JanDupal] Great #JRuby talk by @headius at FI MUNI. Gonna try Java libs with Ruby. Thank you!
 
[ridak_zemlo] Regular Expression Pocket Reference: Regular Expressions for Perl, Ruby, PHP, Python, C, Java and .NET (Pocket R... http://t.co/f6g0ODRX
 
[JobsInTravelMe] CONTRACT – Senior Software Systems Engineering (Ruby &amp; Java) http://t.co/cte4ylfe
(以下略)

環境。


$ uname -mrsv
Darwin 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun  7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386
 
$ g++ --version
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

そして、ありがたい説明。

--------------------------------
value v;
std::string err;
parse(v, json, json + strlne(json), &err);
--------------------------------

第2パラメータについてはイテレータによりパース後の位置に移動するので、破壊的にしたくない場合は一度他の変数に預けるのが良いかと思います。型はnull, boolean, number, string, array, object が使えます。これらを束ねてvalueという型が存在します。
パースするとvalueが得られ、テンプレートを使用してリフレクションできます。

--------------------------------
v.is<std::string>(); // string
v.is<array>(); // array
v.is<object>(); // object
...
--------------------------------

実際の型に変換するにはgetを使います。

--------------------------------
v.get<string>(); // string
v.get<array>(); // array
v.get<object>(); // object
--------------------------------

またstringはto_str()により、stlのstd::stringが得られるので

--------------------------------
std::string s = v.get<string>().to_str()
--------------------------------

と扱えます。arrayはvector<value>なので

--------------------------------
array a = v.get<array>();
for (array::const_iterator it = a.begin(); it != a.end(); it++) {
cout << it->to_str() << endl;
}
--------------------------------

と書く事が出来ます。objectはmap<std::string, value>なので

--------------------------------
object o = v.get<object>();
for (object::const_iterator it = o.begin(); it != o.end(); it++) {
cout << it->first << "=" << it->second.to_str() << endl;
}
--------------------------------

と書く事が出来ます。jsonの構造が分かっている場合はisを使用せずに書くことも出来ますが、型が異なると実行時例外が発生します。
またシリアライズも出きるので

--------------------------------
v.serialize();
--------------------------------

でJSON文字列が得られます。
サンプルはリポジトリのexamplesディレクトリに入れてありますので、参考になるか分かりませんが見てください。

picojsonの使い方が分かる方いませんか? - Yahoo!知恵袋

Ref.
- Kazuho@Cybozu Labs: 今更 C++ で JSON パーサ「picojson」を書いたわけ
- Big Sky :: ヘッダファイルだけでC++から使えるJSONパーサ「picojson」が凄い!
- picojsonの使い方が分かる方いませんか? - Yahoo!知恵袋
- /lang/cplusplus/picojson/trunk/picojson.h – CodeRepos::Share – Trac
- share - Revision 39079: /lang/cplusplus/picojson/trunk/examples

tags: json c++

Posted by NI-Lab. (@nilab)