2011年5月28日土曜日

JSON Parser

http://d.hatena.ne.jp/aonyx/20090618/ より

boostをばりっばりにつかってる

JSONParser (2)
spirit
Boost.Variant を使って実装してみた。
#ifndef SANDRA_RAW_DATA_JSON_HPP
#define SANDRA_RAW_DATA_JSON_HPP
#include
#include
#include
#include
#include
namespace sandra {
namespace raw_data {
struct JSON
{
struct Null { };
struct Object;
typedef boost::make_recursive_variant<
bool,
int,
double,
std::string,
std::deque,
boost::recursive_wrapper,
boost::recursive_wrapper
>::type value_type;
typedef std::string string_type;
typedef std::deque array_type;
typedef Object object_type;
typedef Null null_type;
struct Object
{
typedef std::pair member_type;
typedef std::map map_type;
map_type members;
};
value_type root;
};
} // namespace raw_data.
} // namespace sandra.
#endif // SANDRA_RAW_DATA_JSON_HPP
#ifndef SANDRA_RAW_DATA_JSONPARSER_HPP
#define SANDRA_RAW_DATA_JSONPARSER_HPP
#include
#include
#include
#include
#include
#include "json.hpp"
namespace sandra {
namespace raw_data {
class JSONParser :
public boost::spirit::grammar
{
public:
struct Data
{
typedef boost::shared_ptr any_ptr_type;
typedef std::stack stack_type;
struct ArrayDelimiter { };
struct ObjectDelimiter { };
stack_type value_stack;
template
void
push(const T& value)
{ value_stack.push(any_ptr_type(new boost::any(value))); }
void
push_array_delimiter()
{ value_stack.push(any_ptr_type(new boost::any(ArrayDelimiter()))); }
void
push_object_delimiter()
{ value_stack.push(any_ptr_type(new boost::any(ObjectDelimiter()))); }
JSON::value_type
get_root() const
{
any_ptr_type value = value_stack.top();
return *boost::any_cast(value.get());
}
};
class PushBool
{
public:
PushBool(const JSONParser& self, bool value) :
self_(self),
value_(value)
{ }
template
void
operator()(Iterator /*first*/, Iterator /*last*/) const
{
JSON::value_type json_value = value_;
self_.data->push(json_value);
}
private:
const JSONParser& self_;
bool value_;
};
class PushNumber
{
public:
explicit
PushNumber(const JSONParser& self) :
self_(self)
{ }
template
void
operator()(Num value) const
{
JSON::value_type json_value = value;
self_.data->push(json_value);
}
private:
const JSONParser& self_;
};
class PushString
{
public:
explicit
PushString(const JSONParser& self) :
self_(self)
{ }
template
void
operator()(Iterator first, Iterator last) const
{
JSON::value_type json_value = std::string(first, last);
self_.data->push(json_value);
}
private:
const JSONParser& self_;
};
class PushNull
{
public:
explicit
PushNull(const JSONParser& self) :
self_(self)
{ }
template
void
operator()(Iterator /*first*/, Iterator /*last*/) const
{
JSON::value_type json_value = JSON::null_type();
self_.data->push(json_value);
}
private:
const JSONParser& self_;
};
class BeginArray
{
public:
explicit
BeginArray(const JSONParser& self) :
self_(self)
{ }
template
void
operator()(Num /*value*/) const
{ self_.data->push_array_delimiter(); }
private:
const JSONParser& self_;
};
class EndArray
{
public:
explicit
EndArray(const JSONParser& self) :
self_(self)
{ }
template
void
operator()(Num /*value*/) const
{
JSON::array_type array;
while(!self_.data->value_stack.empty()) {
Data::any_ptr_type element = self_.data->value_stack.top();
self_.data->value_stack.pop();
if(boost::any_cast(element.get())) {
break;
}
JSON::value_type json_value =
*boost::any_cast(element.get());
array.push_front(json_value);
}
JSON::value_type json_value = array;
self_.data->push(json_value);
}
private:
const JSONParser& self_;
};
class MakeMember
{
public:
explicit
MakeMember(const JSONParser& self) :
self_(self)
{ }
template
void
operator()(Iterator /*first*/, Iterator /*last*/) const
{
Data::any_ptr_type value = self_.data->value_stack.top();
self_.data->value_stack.pop();
Data::any_ptr_type key = self_.data->value_stack.top();
self_.data->value_stack.pop();
JSON::value_type json_key =
*boost::any_cast(key.get());
JSON::value_type json_value =
*boost::any_cast(value.get());
std::string member_name = boost::get(json_key);
JSON::Object::member_type member =
std::make_pair(member_name, json_value);
self_.data->push(member);
}
private:
const JSONParser& self_;
};
class BeginObject
{
public:
explicit
BeginObject(const JSONParser& self) :
self_(self)
{ }
template
void
operator()(Num /*value*/) const
{ self_.data->push_object_delimiter(); }
private:
const JSONParser& self_;
};
class EndObject
{
public:
explicit
EndObject(const JSONParser& self) :
self_(self)
{ }
template
void
operator()(Num /*value*/) const
{
JSON::object_type object;
while(!self_.data->value_stack.empty()) {
Data::any_ptr_type element = self_.data->value_stack.top();
self_.data->value_stack.pop();
if(boost::any_cast(element.get())) {
break;
}
JSON::Object::member_type member =
*boost::any_cast(element.get());
object.members.insert(member);
}
JSON::value_type json_value = object;
self_.data->push(json_value);
}
private:
const JSONParser& self_;
};
template
class definition
{
public:
typedef boost::spirit::rule rule_type;
explicit
definition(const JSONParser& self)
{
using namespace boost::spirit;
value_r
= bool_r
| strict_real_p[PushNumber(self)]
| int_p[PushNumber(self)]
| string_r
| array_r
| object_r
| null_r
;
bool_r
= str_p("true")[PushBool(self, true)]
| str_p("false")[PushBool(self, false)]
;
string_r
= confix_p('"', (*c_escape_ch_p)[PushString(self)], '"')
;
array_r
= ch_p('[')[BeginArray(self)]
>> list_p(value_r, ',')
>> ch_p(']')[EndArray(self)]
;
object_r
= ch_p('{')[BeginObject(self)]
>> list_p(member_r, ',')
>> ch_p('}')[EndObject(self)]
;
member_r
= (string_r >> ch_p(':') >> value_r)[MakeMember(self)]
;
null_r
= str_p("null")[PushNull(self)]
;
}
const rule_type&
start() const
{ return value_r; }
private:
rule_type value_r;
rule_type bool_r;
rule_type string_r;
rule_type array_r;
rule_type member_r;
rule_type object_r;
rule_type null_r;
};
Data* data;
explicit
JSONParser(Data* data) :
data(data)
{ }
};
} // namespace raw_data.
} // namespace sandra.
#endif // SANDRA_RAW_DATA_JSONPARSER_HPP
#include
#include
#include
#include
#include
#include
namespace {
class JSONPrinter :
public boost::static_visitor<>
{
public:
JSONPrinter(int tab=0) :
tab_(tab)
{ }
void
operator()(bool value) const
{ std::cout << (value ? "true" : "false"); }
void
operator()(int value) const
{ std::cout << value; }
void
operator()(double value) const
{ std::cout << value; }
void
operator()(const std::string& value) const
{ std::cout << '"' << value << '"'; }
void
operator()(const sandra::raw_data::JSON::array_type& value) const
{
std::cout << '[';
for(std::size_t i = 0; i < value.size(); ++i) {
if(i != 0) {
std::cout << ", ";
}
boost::apply_visitor(JSONPrinter(tab_), value.at(i));
}
std::cout << ']';
}
void
operator()(const sandra::raw_data::JSON::object_type& value) const
{
using namespace sandra::raw_data;
std::cout << "{\n";
put_tab_(tab_ + 1);
bool first = true;
for(JSON::Object::map_type::const_iterator i = value.members.begin(),
last = value.members.end(); i != last; ++i) {
if(first) {
first = false;
}
else {
std::cout << ",\n";
put_tab_(tab_ + 1);
}
std::cout << '"' << i->first << "\": ";
boost::apply_visitor(JSONPrinter(tab_ + 1), i->second);
}
std::cout << "\n";
put_tab_(tab_);
std::cout << '}';
}
void
operator()(const sandra::raw_data::JSON::null_type& /*value*/) const
{
std::cout << "null";
}
private:
void
put_tab_(int n) const
{
for(int i = 0; i < n * 2; ++i) {
std::cout << ' ';
}
}
private:
int tab_;
};
} // unnamed namespace.
int
main(int argc, char* argv[])
{
using namespace sandra::raw_data;
typedef boost::spirit::file_iterator iterator;
typedef boost::spirit::position_iterator position_iterator;
if(argc < 2) {
return EXIT_FAILURE;
}
iterator begin(argv[1]);
if(!begin) {
return EXIT_FAILURE;
}
iterator end(begin.make_end());
JSON json;
boost::spirit::parse_info info =
parse_json(&json,
position_iterator(begin, end, argv[1]),
position_iterator());
if(!info.hit) {
boost::spirit::file_position position = info.stop.get_position();
std::cerr << position.file << ":"
<< position.line << "("
<< position.column << ") parse error." << std::endl;
return EXIT_FAILURE;
}
boost::apply_visitor(JSONPrinter(), json.root);
std::cout << std::endl;
return EXIT_SUCCESS;
}
が、Visual C++ 2008 Express Edition だと json.hpp でコンパイルエラー(C2027)が出る。
Actually there is an easy hack to make this work.
Just add:
struct boost::recursive_variant_ {};
http://marc.info/?l=boost&m=118835851573466
これを参考に
#include
#include
+struct boost::recursive_variant_ { };
+
namespace sandra {
namespace raw_data {
とすれば Visual C++ 2008 Express Edition でもコンパイルが通るようになる。

0 件のコメント:

コメントを投稿