2015年2月21日土曜日

JSONデータのハンドリング:c++

さてこれまでブラウザ上でJSONデータの扱いを調べてきましたが、これらは入力データの編集用と思って調べてきました。こっからが本題のc++によるJSONデータの扱いです。

もともと、本職はc++プログラムがほとんどなんで、いつも入出力のGUIに悩まされていました。それをCouchDBで楽したいというところで、色々調べていましたがだいたい目処がついてきたので、さてそれをc++プログラムでどうやってハンドリングしたらいいのか?というところまできました。

c++でJSONのパーサはいくつか見つかりましたが、picojsonがヘッダファイル一つというシンプルさでなかなか秀逸です。早速これを試してみました。

まず試験データとしてのJSONファイル(test3.json)です。

  1. {"message":"success",
  2. "number":[1, 2],
  3. "person":{"name":"hoge", "age":38},
  4. "follow":[
  5. {"name":"hoge", "age":38},
  6. {"name":"huga", "age":42}
  7. ],
  8. "returnCode":0}

わざと複雑な階層構造を持たせています。このファイル名を引数として動くサンプルが以下です。

  1. #include
  2. #include
  3. #include "picojson.h"
  4. int main(int argc, char** argv)
  5. {
  6. picojson::value v;
  7. std::ifstream infile;
  8. std::string inStr = "";
  9. infile.open(argv[1]);
  10. while (!infile.eof()) {
  11. std::string work = "";
  12. infile >> work;
  13. inStr += work;
  14. }
  15. infile.close();
  16. std::string err = parse(v, inStr);
  17. if (!err.empty()) {
  18. std::cerr << picojson::get_last_error() << std::endl;
  19. return 1;
  20. }
  21. // dump json object
  22. std::cout << "---- dump input ----" << std::endl;
  23. std::cout << v << std::endl;
  24. // accessors
  25. std::cout << "---- analyzing input ----" << std::endl;
  26. if (v.is()) {
  27. std::cout << "input is an object" << std::endl;
  28. //const picojson::object& o = v.get();
  29. picojson::object& o = v.get();
  30. for (picojson::object::const_iterator i = o.begin(); i != o.end(); ++i) {
  31. std::cout << i->first << " " << i->second << std::endl;
  32. }
  33. std::cout << std::endl;
  34. // 一番外側のobjectの取得
  35. std::string& s1 = o["message"].get();
  36. //picojson::array& s1 = o["number"].get();
  37. std::cout << s1 << std::endl;
  38. std::cout << std::endl;
  39. // 値の変更
  40. o["message"] = (picojson::value)std::string("fail");
  41. // データの追加
  42. o.insert(std::map::value_type("piyopiyo", 10.0));
  43. for (picojson::object::const_iterator i = o.begin(); i != o.end(); ++i) {
  44. std::cout << i->first << " " << i->second << std::endl;
  45. }
  46. std::cout << std::endl;
  47. // 内部のobject値の取得
  48. picojson::object& o1 = o["person"].get();
  49. std::cout << o1["name"].get() << " " << o1["age"].get() << std::endl;
  50. std::cout << std::endl;
  51. // array値の取得
  52. picojson::array& a1 = o["follow"].get();
  53. o1 = a1[1].get();
  54. std::cout << o1["name"].get() << " " << o1["age"].get() << std::endl;
  55. std::cout << std::endl;
  56. // serialize(JSON形式の文字列に戻す。JavaScriptのstringify()と同じ。)
  57. std::string str = picojson::value(o).serialize();
  58. std::cout << str << std::endl;
  59. }
  60. return 0;
  61. }

実行すると以下の様な出力をだしてきます。

  1. ---- dump input ----
  2. {"follow":[{"age":38,"name":"hoge"},{"age":42,"name":"huga"}],"message":"success","nu\
  3. mber":[1,2],"person":{"age":38,"name":"hoge"},"returnCode":0}
  4. ---- analyzing input ----
  5. input is an object
  6. follow [{"age":38,"name":"hoge"},{"age":42,"name":"huga"}]
  7. message "success"
  8. number [1,2]
  9. person {"age":38,"name":"hoge"}
  10. returnCode 0
  11.  
  12. success
  13.  
  14. follow [{"age":38,"name":"hoge"},{"age":42,"name":"huga"}]
  15. message "fail"
  16. number [1,2]
  17. person {"age":38,"name":"hoge"}
  18. piyopiyo 10
  19. returnCode 0
  20.  
  21. hoge 38
  22.  
  23. huga 42
  24.  
  25. {"follow":[{"age":38,"name":"hoge"},{"age":42,"name":"huga"}],"message":"fail","numbe\
  26. r":[1,2],"person":{"age":42,"name":"huga"},"piyopiyo":10,"returnCode":0}

うまく動いていますね。階層構造の扱いも、値の変更も思い通りになります。
ちょっと面倒くさいのが、いちいちpicojson::value形式で読みこんだ後、データの操作のためにpicojson::object形式にしないといけないこと。JavaScriptでも同じような考え方でしたが何でですかね?picojsonの実装はヘッダファイル一つなので、少し読んでみると、picojson::valueではvector形式で読み込んでいるようですが、すぐにmap形式にしてしまいます。確かにこれならアクセスするのが簡単で、処理速度も早いんですがデータ項目の順番がkeyでソートされてしまい順番が変わってしまいます。JSONとしては問題ないんですが、デバッグ時に少し悩みそうです。

後、このサンプルではparse()関数を使っていますが、picojson::valueは、>>を使って直接読み込むことができます。わざわざサンプル用に、stringでファイルを読み込み、parse()関数を使うように変更しました。こっちの方がエラーの扱いがわかりやすいからなんですがね。

picojsonなかなかいいんですが、一つ難点を言えば、c++のtemplate機能を使い倒しているため、エラーが出てくると、わけのわからないエラーメッセージを出してくるのがつらいです。c++の機能なんで、しかたないんですが。


0 件のコメント:

コメントを投稿