TinyJSONを使ってみる

Boost.Spiritを使ったJSONパーサーであるTinyJSON
http://blog.beef.de/projects/tinyjson/
を試してみた。
サンプルコードに乏しいのとサイトのUsageにある
traverse関数の宣言が間違っていてコンパイルが通らないという問題により
無駄に敷居が上がっているがこれはいいものだ。
とりあえずtraverseが動くようにするには、

void traverse(const json::grammar<char>::variant &var)

としてarrayをvariantに変える。


標準入力からJSONを読み込んでパースするサンプルを作った。
配布元のサンプルのtraverseを改造したものです。

#include <iostream>
#include <vector>
#include "tinyjson.hpp"

void traverse(const json::grammar<char>::variant &var, const std::string &indent="")
{
  std::cout << indent;
  if(var->empty())
  {
    // variant is empty => it's a "null" value
    std::cout << "<null>";
  }
  else if(var->type() == typeid(bool))
  {
    // variant is of type "bool"...
    bool bValue = boost::any_cast< bool >(*var);
    std::cout << "<bool> " << bValue << std::endl;
  }
  else if(var->type() == typeid(int))
  {
    // variant is of type "int"...
    int iValue = boost::any_cast< int >(*var);
    std::cout << "<int> " << iValue << std::endl;
  }
  else if(var->type() == typeid(double))
  {
    // variant is of type "double"...
    double dValue = boost::any_cast< double >(*var);
    std::cout << "<double> " << dValue << std::endl;
  }
  else if(var->type() == typeid(std::string))
  {
    // variant is a string...
    std::string strValue = boost::any_cast< std::string >(*var);
    std::cout << "<string> " << strValue << std::endl;
  }
  else if(var->type() == typeid(json::grammar<char>::array))
  {
    // variant is an array => use recursion
    json::grammar<char>::array const & a = boost::any_cast< json::grammar<char>::array >(*var);

    std::cout << "<array> [" << std::endl;
    for(json::grammar<char>::array::const_iterator it = a.begin(); it != a.end(); ++it)
    {
      traverse(*it, indent+"  ");
    }
    std::cout << indent << "]" << std::endl;;
  }
  else if(var->type() == typeid(json::grammar<char>::object))
  {
    // variant is an object => use recursion
    json::grammar<char>::object const & o = boost::any_cast< json::grammar<char>::object >(*var);

    std::cout << "<object> {" << std::endl;
    for(json::grammar<char>::object::const_iterator it = o.begin(); it != o.end(); ++it)
    {
      std::string strName = (*it).first;
      std::cout << indent << '"' << strName << '"' << std::endl;
      traverse((*it).second, indent+"  ");
    }
    std::cout << indent << "}" << std::endl;
  }
  else
  {
    // ERROR: unknown type...
    std::cout << "*unknown*" << std::endl;
  }
}

int main()
{
  // input
  std::vector<char> input;
  input.assign(std::istream_iterator<char>(std::cin), std::istream_iterator<char>());

  // parse json
  json::grammar<char>::variant var = json::parse(input.begin(), input.end());

  // config float output
  std::cout.setf(std::ios::fixed);

  // result
  traverse(var);
}

ちょっとboost::anyのうれしさがわかった。

使い方は標準入力にJSONテキストを流し込む

$ echo '{"prop":[1, 2, 3]}' | sample
<object> {
  "prop"
    <array> [
    <int> 1
    <int> 2
    <int> 3
    ]
}

$ sample < hoge.json

TinyJSON使用上の注意点

  • 改行コードは\nのみ。\r\nだとパースに失敗するっぽい?(std::endlが入ったストリームはだめだった)
  • 一番外を{}にする。いきなり数字や[]を投入できない
  • Objectのキーも"でクォートする。{name:"hoge"}じゃなくて{"name":"hoge"}


これでわりとありとあらゆる情報をファイルに書き出してそれを再構築できる予定。
JSONであれば読み書きの順番を気にする必要がないのと、
わりと可読性があるので手での修正がしやすいのがよいところ。
さらにTinyJSONはヘッダのみなのでリンクしなくていいのもよい。
あとソースもきれいでよい。まだ、読んでないけども。