Fork me on GitHub

2011/03/19


このエントリーをはてなブックマークに追加
以前、kazuhoさんが書いたpicojsonの紹介をさせて頂いたのですが、あれから何度も使わせて頂いていてkazuhoさんのいる方角には足を向けて寝られないmattnです。
Big Sky :: ヘッダファイルだけでC++から使えるJSONパーサ「picojson」が凄い!

kazuhoさんがやってくれました。ずいぶん前からjsonをC++でパース(SAXじゃなくてDOM)するのに小さいライブラリないかなーと思ってました。個人的にはjson-cというのを使ってたのですが...

http://mattn.kaoriya.net/software/lang/c/20090702153947.htm
なんど見ても綺麗なコードです。
で、先日ちょっと出張があって新幹線に乗る事があり、ちょっとルールを決めて僕にも同じ物が書けるか試してみました。
以下ルール。
  • picojson相当のjsonパーサをC++で書く
  • インターネット等の外部リソースは見てはいけない(picojsonも見ちゃダメ)
  • 見て良いのは/usr/include以下だけ
  • 制限時間は新幹線に乗っていられる間だけ
このルールで新幹線に乗った途端、ノートPCを開いて書き始めました。いろいろ書いては直してを繰り返し、出来上がった物を公開しておきます。おそらく、picojsonと同じ様な動きをします。
但し、後からpicojsonを見て気づいたのですが、"\uXXXX"といった文字列表記に対応出来ていません。まぁもう外部リソースを見てしまったので、これから対応するつもりはありません。
一応、マルチブートのノートPCだったのでWindowsでもコンパイル出来る様にしてあります。
あと、オブジェクトのリテラルで最後がカンマで終わる様なフォーマットも#define宣言でパース出来る様にしました。さらに関数テンプレートが使えないVC6でもビルド出来る様になっています。(使い方が若干異なりますが)

picojsonと同じようにヘッダファイルだけで使えます。ただしkazuhoさん程のC++erでは無いので抜けがあるかもしれませんが...苦笑。

githubに適当にアップしておきました。
mattn/minijson - GitHub

simple C++ json library. (clone of kazuho/picojson)

https://github.com/mattn/minijson
ネタですが、よろしければ話題のネタ程度にどうぞ。
Posted at 23:11 in ソフトウェア::lang::c
Tagged as: c++, json, picojson
Bookmarks: add to hatena add to hatena | add to delicious.com | add to livedoor.clip add to livedoor.clip

2009/07/02


このエントリーをはてなブックマークに追加
kazuhoさんがやってくれました。
ずいぶん前からjsonをC++でパース(SAXじゃなくてDOM)するのに小さいライブラリないかなーと思ってました。個人的にはjson-cというのを使ってたのですが、幾らか気に入らない所があったりビルドが少し手間だったりしていました。STLしか使わなくてvectorとかmapで表現されるツリー構造な物が欲しいなぁって思ってたんです。
とあるIRCで昨日、kazuhoさんと「ほしいですよねー」という話から始まって、githubにあるjsonxxとかも物色しながら「いいのないねー」とか言ってたらkazuhoさんが「もすこし綺麗に書けそう」って言い出して朝から本格的に書き始めてついさっき出来上がりました。速いw
名前はpicojson
とても小さく、実装コードだと300数十ステップ程です。しかもヘッダファイルだけなので管理が楽です。

試しにwassrのpublicタイムラインをパースしてみました。
コードはこんな感じ。
curlのコードではなく、jsonのパース部分を見てください。
#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[]) {
  char error[256];

  MEMFILE* mf = memfopen();
  CURL* curl = curl_easy_init();
  curl_easy_setopt(curl, CURLOPT_URL, "http://api.wassr.jp/statuses/public_timeline.json");
  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()) {
      array arr = v.get<array>();
      array::iterator it;
      for (it = arr.begin(); it != arr.end(); it++) {
        object obj = it->get<object>();
        cout << obj["user_login_id"].to_str() << ": " << obj["text"].to_str() << endl;
      }
    } else {
      cerr << err << endl;
    }
  }
  curl_easy_cleanup(curl);
  memfclose(mf);

  return 0;
}
こんなに短いコードでアプリが作れる!
STLに慣れた人ならイメージ沸くかと思います。すばらしい!
こういうのが欲しかったんです。
ただまだ出来上がったばっかりですしバグはあるかもしれません。また高機能にするつもりもないでしょうから使用目的を選ぶのが先決かと思います。
ライセンスはBSDとの事なので、バイナリ配布も可能です。

share - Revision 34226: /lang/cplusplus/picojson/trunk

picojson

http://svn.coderepos.org/share/lang/cplusplus/picojson/trunk/
ありがたや、ありがたや。

追記
kazuhoさんも記事を書いてますんでそちらも...
Kazuho@Cybozu Labs: 今更 C++ で JSON パーサ「picojson」を書いたわけ
http://developer.cybozu.co.jp/kazuho/2009/07/c-json-picojson.html
Posted at 15:49 in ソフトウェア::lang::c
Tagged as: c++, cpp, json
Bookmarks: add to hatena add to hatena | add to delicious.com | add to livedoor.clip add to livedoor.clip

2009/04/14


このエントリーをはてなブックマークに追加
JSONって大体のサーバレスポンスだと、改行もなくエディタで開いてちまちま改行入れて内容確認...なんて事やってる人もいるかと思います。
結構前に作った物ですが、もしかしたら便利と思ってくれる人もいるんじゃないか...と思ったの晒しておきます。
mattn's gtkjsonviewer at master - GitHub

A simple json viewer written in GTK.

http://github.com/mattn/gtkjsonviewer/tree/master
早い話が、JSONをGUIで見ようという話です。中身は簡単なpythonのコードで、GTKを使っています。
# curl http://twitter.com/public_timeline/?format=json | gtkjsonview.py
の様に使います。Windowsの場合はpython.exeがちゃんと標準入力を読み取ってくれないので
# curl http://twitter.com/statuses/user_timeline/mattn_jp.json | python gtkjsonview.py
とする必要があります。起動すると以下の様な画面が表示されます。
gtkjsonview
不正なJSON(例えば最終カンマあり)等の場合はパースエラーが出ます。
Posted at 04:29 in ソフトウェア::lang::python
Tagged as: gtk, json
Bookmarks: add to hatena add to hatena | add to delicious.com | add to livedoor.clip add to livedoor.clip

2009/03/11


このエントリーをはてなブックマークに追加
readline使ってコマンドライン提供、curlで通信、結果をjson-cでパースまで作った。燃え尽きた。
追記
shebangから使えるようにした。
#!/usr/bin/dansh
以下コード
// for MSVC: cl -I.. /Tp dansh.cpp curl.lib readline.lib ..\Release\json.lib
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
namespace json {
#include "json.h"
}
#define READLINE_STATIC
#include <readline/readline.h>
#include <curl/curl.h>
#define API_URL "http://api.dan.co.jp/perleval.cgi?c=callback&s="

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

char*
url_encode_alloc(const char* str, int force_encode) {
    const char* hex = "0123456789abcdef";

    char* buf = NULL;
    unsigned char* pbuf = NULL;
    int len = 0;

    if (!str) return NULL;
    len = strlen(str)*3;
    buf = (char*) malloc(len+1);
    memset(buf, 0, len+1);
    pbuf = (unsigned char*)buf;
    while(*str) {
        unsigned char c = (unsigned char)*str;
        if (c == ' ')
            *pbuf++ = '+';
        else if (c & 0x80 || force_encode) {
            *pbuf++ = '%';
            *pbuf++ = hex[c >> 4];
            *pbuf++ = hex[c & 0x0f];
        } else
            *pbuf++ = c;
        str++;
    }
    return buf;
}

void do_dan(const char* line) {
    char* source = url_encode_alloc(line, TRUE);
    char* url = (char*) malloc(strlen(API_URL) + strlen(source) + 1);
    strcpy(url, API_URL);
    strcat(url, source);

    MEMFILE* mf = memfopen();
    CURL* curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
    CURLcode res = curl_easy_perform(curl);
    free(url);
    if (res == CURLE_OK) {
        char* data = memfstrdup(mf);
        memfclose(mf);

        // remove callback function
        char* ptr;
        ptr = strrchr(data, ')');
        if (ptr) *ptr = 0;
        ptr = strchr(data, '(');
        if (ptr) *ptr++ = 0;
        else ptr = data;

        json::json_object *obj = json::json_tokener_parse(ptr);
        if (!is_error(obj)) {
            json::json_object *result = json::json_object_object_get(obj, "result");
            if (!is_error(result)) {
                printf("%s\n", json::json_object_to_json_string(result));
                json_object_put(result);
            }
        }
        free(data);
    }
    curl_easy_cleanup(curl);
}

int main(int argc, char **argv)
{
    char* line = NULL;

    //json::mc_set_debug(1);
    if (argc == 2) {
        FILE* fp = fopen(argv[1], "rb");
        if (!fp) {
            perror("can't open file");
            exit(-1);
        }
        char buf[BUFSIZ];
        while (fgets(buf, sizeof(buf), fp)) {
            if (!line) {
                if (strncmp(buf, "#!", 2))
                    line = strdup(buf);
            } else {
                line = (char*) realloc(line, strlen(line) + strlen(buf) + 1);
                strcat(line, buf);
            }
        }
        fclose(fp);
        do_dan(line);
        free(line);
    }
    if (!isatty(fileno(stdin))) {
        char buf[BUFSIZ];
        while (fgets(buf, sizeof(buf), stdin)) {
            if (!line) {
                if (strncmp(buf, "#!", 2))
                    line = strdup(buf);
            } else {
                line = (char*) realloc(line, strlen(line) + strlen(buf) + 1);
                strcat(line, buf);
            }
        }
        do_dan(line);
        free(line);
    } else {
        while (line = readline("dan> ")) {
            do_dan(line);
            free(line);
        }
    }
    return 0;
}

参考文献: 404 Blog Not Found:Ajax - perlを実行するAPI
Posted at 14:01 in ソフトウェア::lang::c
Tagged as: c, curl, json, readline
Bookmarks: add to hatena add to hatena | add to delicious.com | add to livedoor.clip add to livedoor.clip