我正在寻找一种存储JSON结构的方法
{
"foo" : "FOO" ,
"fuu" : "FUU" ,
"bar" :
{
"no" : "abc" ,
"yes" : "ABC"
} ,
"baa" :
{
"no" : "xyz" ,
"yes" : "XYZ"
}
}
作为boost::assign:::map_list_of
初始化的C ++中的地图。像这样的东西
const std::map<UNKNOWN_TYPE_INVOLVING_VARIANT> = boost::assign::map_list_of
{
{ "foo" , "FOO" } ,
{ "fuu" , "FUU" } ,
{ "bar" ,
{ "no" , "abc" } ,
{ "yes" , "ABC" }
} ,
{ "baa" ,
{ "no" , "xyz" } ,
{ "yes" , "XYZ" }
}
};
其中一些键映射到字符串,其他键映射到子映射(hello variant)。请注意,我仅限于C ++ 03,我可以使用其他解决方案,例如Boost Proto(DSL)。
答案 0 :(得分:1)
免责声明:我认为这可能是一种很酷的方法,但遗憾的是我没有意识到(使用typedefs
和丑陋的for
循环作为证人) BOOST_METAPARSE_STRING
需要c ++ 11。所以这并没有解决问题中的问题。
以下方法使用从Boost 1.61开始应该可用的Boost.Metaparse(Docs)。它使用一个宏(JSON_MAP_ASSIGNER
),允许您直接使用问题中的原始JSON结构(注意重复的括号)。
Map json = JSON_MAP_ASSIGNER((
{
"foo" : "FOO" ,
"fuu" : "FUU" ,
"bar" :
{
"no" : "abc" ,
"yes" : "ABC"
} ,
"baa" :
{
"no" : "xyz" ,
"yes" : "XYZ"
}
}
));
这种方法有几个问题:
- 它不能使用运行时字符串,必须在编译时知道。
- 编译器可能会用足够大的json字符串来阻塞。
宏从&#34; tuple&#34;创建一个字符串,解析该字符串,获取类型(map_assigner<vector<pair_assigner...> >
)并实例化该类型。该实例被分配给地图,并负责构建地图。
完整代码 (Running on Wandbox)
#define BOOST_METAPARSE_LIMIT_STRING_SIZE 128
#include <iostream>
#include <string>
#include <map>
#include<boost/variant.hpp>
#include <boost/mpl/quote.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/string.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/metaparse/alphanum.hpp>
#include <boost/metaparse/string.hpp>
#include <boost/metaparse/lit_c.hpp>
#include <boost/metaparse/token.hpp>
#include <boost/metaparse/first_of.hpp>
#include <boost/metaparse/build_parser.hpp>
#include <boost/metaparse/entire_input.hpp>
#include <boost/metaparse/foldl_start_with_parser.hpp>
#include <boost/metaparse/transform.hpp>
#include <boost/metaparse/one_of.hpp>
#include <boost/metaparse/last_of.hpp>
#include <boost/metaparse/middle_of.hpp>
#include <boost/metaparse/sequence.hpp>
namespace assign_helper
{
using namespace boost::metaparse;
namespace mpl = boost::mpl;
// map_assigner and pair assigner are the two classes that actually assign to the map
//BEGIN ASSIGNERS
template <typename PairSequence>
struct map_assigner
{
template<class K, class V, class C, class A>
struct do_assign
{
do_assign(std::map<K, V, C, A>& val) :map_(val) {};
std::map<K, V, C, A>& map_;
template <typename Pair>
void operator()(Pair) const
{
Pair::template assign(map_);
}
};
//This does the actual work when an instance of map_assigner is assigned to a map
template<class K, class V, class C, class A>
operator std::map<K, V, C, A>() const
{
//Creates a map...
std::map<K, V, C, A> m;
//...and assigns each of the pairs
mpl::for_each<PairSequence>(do_assign<K, V, C, A>(m));
return m;
}
};
//if it's a "normal" pair (string,string)
template <typename First, typename Second>
struct pair_assigner
{
template <typename K, typename V, typename C, typename A>
static void assign(std::map<K, V, C, A>& m)
{
m[mpl::c_str<First>::type::value] = mpl::c_str<Second>::type::value;
}
};
//if it's a pair that has a map as its second param
template <typename First, typename NestedSequence>
struct pair_assigner<First, map_assigner<NestedSequence> >
{
template <typename K, typename V, typename C, typename A>
static void assign(std::map<K, V, C, A>& m)
{
std::map<K, V, C, A> nested_m = map_assigner<NestedSequence>();
m[mpl::c_str<First>::type::value] = nested_m;
}
};
//END ASSIGNERS
//BEGIN AST-BUILD HELPERS
//This is "called" with a mpl::vector<string,char<':'>,value> (value can be either string or another map)
//and "creates" a pair_assigner<string,value>
template <typename Sequence>
struct create_pair_assigner : pair_assigner<typename mpl::at_c<Sequence, 0>::type, typename mpl::at_c<Sequence, 2>::type>
{};
//This is "called" with a char<letter>
//and "creates" a string<letter> which can be appended to
template <typename Letter>
struct create_string : string<Letter::value>
{};
//This is "called" with an Elem
//and "creates" a vector<Elem> which can be appended to
template <typename Elem>
struct create_vector : mpl::vector1<Elem>
{};
//This is "called" with a vector/string and an Elem/char<letter> and appends the latter to the former
template <typename State, typename Elem>
struct append : mpl::push_back<State, Elem>
{};
//END AST BUILD HELPERS
//BEGIN TOKENS
typedef token < lit_c < '(' > > paren_open;
typedef token < lit_c < ')' > > paren_close;
typedef token < lit_c < '{' > > brace_open;
typedef token < lit_c < '}' > > brace_close;
typedef token < lit_c < ',' > > comma;
typedef token < lit_c < ':' > > colon;
typedef lit_c < '"' > quotation;
//END TOKENS
//BEGIN RULES
//This parses and returns a letter
typedef one_of<alphanum, lit_c<'_'>, lit_c<' '> > word_letter;
//This parses a letter optionally followed by more and returns a string
typedef foldl_start_with_parser<
word_letter,
transform<word_letter, mpl::quote1<create_string> >,
mpl::quote2<append> > word;
//This parses a quoted string and returns a string without quotes
typedef token<middle_of<quotation, word, quotation> > literal;
//This is a recursive rule and needs to be forward declared
struct json_body;
//A value is either a string or a map
typedef one_of< literal, json_body > value;
//This parses `string ':' (string|map)` and returns pair_assigner<string,string> or pair_assigner<string,map>
typedef transform<sequence<literal, colon, value>, mpl::quote1<create_pair_assigner> > json_pair;
//This parses a pair optionally followed by groups of (',' pair)
//and returns a map_assigner<vector<pair_assigner...> >
typedef transform<
foldl_start_with_parser<
last_of<comma, json_pair>,
transform<json_pair, mpl::quote1<create_vector> >,
mpl::quote2<append>
>,
mpl::quote1<map_assigner> >pair_sequence;
//This parses `'{' pair_sequence '}'` and returns pair_sequence
struct json_body : middle_of<brace_open, pair_sequence, brace_close>
{};
//This parses `'(' json_body ')'` and returns json_body
//the parentheses are an artifact due to the tuple used to stringize the json text to avoid problems with commas
typedef middle_of<paren_open, json_body, paren_close> json;
//END RULES
typedef build_parser<entire_input<json> > assigner;
}
//Creates a map_assigner and instantiates it
#define CREATE_ASSIGNER(TEXT) assign_helper::assigner::apply<BOOST_METAPARSE_STRING(TEXT)>::type()
#define JSON_MAP_ASSIGNER(TUPLE) CREATE_ASSIGNER(BOOST_PP_STRINGIZE(TUPLE))
typedef boost::make_recursive_variant<std::string, std::map<std::string, boost::recursive_variant_> >::type Value;
typedef std::map<std::string, Value> Map;
struct printer : boost::static_visitor<void>
{
printer(int indent) :indent(indent) {}
void operator()(const std::string& val) const
{
std::cout << std::string(indent, ' ') << val << std::endl;
}
void operator()(const Map& val) const
{
for (Map::const_iterator it = val.begin(), end = val.end(); it != end; ++it)
{
std::cout << std::string(indent, ' ') << it->first << std::endl;
boost::apply_visitor(printer(indent + 4), it->second);
std::cout << std::string(indent, ' ') << std::endl;
}
}
int indent;
};
void print(const Value& val)
{
boost::apply_visitor(printer(0), val);
}
int main()
{
Map json = JSON_MAP_ASSIGNER((
{
"foo" : "FOO" ,
"fuu" : "FUU" ,
"bar" :
{
"no" : "abc" ,
"yes" : "ABC"
} ,
"baa" :
{
"no" : "xyz" ,
"yes" : "XYZ"
}
}
));
print(json);
}
答案 1 :(得分:1)
这是一种基于Boost.Proto的方法,它可以与c ++ 03一起使用。它与文档中的this example略有不同。
它可以像这样使用(遗憾的是不像其他解决方案那样漂亮):
// Initialize a map:
Map json =
map_list_of
("foo", "FOO")
("fuu", "FUU")
("bar",
map_list_of
("no", "abc")
("yes", "ABC")
)
("baa",
map_list_of
("no", "xyz")
("yes", "XYZ")
)
;
Proto构建一个这样的树:
function(
function(
function(
function(
terminal(map_list_of_tag)
, terminal(foo)
, terminal(FOO)
)
, terminal(fuu)
, terminal(FUU)
)
, terminal(bar)
, function(
function(
terminal(map_list_of_tag)
, terminal(no)
, terminal(abc)
)
, terminal(yes)
, terminal(ABC)
)
)
, terminal(baa)
, function(
function(
terminal(map_list_of_tag)
, terminal(no)
, terminal(xyz)
)
, terminal(yes)
, terminal(XYZ)
)
)
并根据匹配的表达式应用不同的变换。有四种不同的情况:
function(map_list_of,terminal,terminal)
:这是最简单的情况。在匹配 map_list_of("foo","FOO")
时会出现示例。只需在地图中插入一对(键,值)并返回参考。
function(function,terminal,terminal)
:匹配任何不是第一个终端的终端(map_list_of("foo","FOO")
("fuu","FUU")
)时会发生这种情况。将一对(键,值)插入到从递归地将变换应用于第一个子项返回的映射中。
function(map_list_of,terminal,function)
:如果第一对括号中有嵌套映射,则会发生这种情况。它创建嵌套地图,然后将其与原始地图中的钥匙终端一起插入。
function(function,terminal,function)
:将带括号的组与嵌套地图(map_list_of("foo", "FOO")("fuu", "FUU")
("bar", map_list_of("no", "abc")("yes", "ABC"))
)匹配时会出现这种情况。创建嵌套映射并将其插入bar
键。
完整代码 (Running on Coliru)
#include <map>
#include <string>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/transform.hpp>
#include <boost/type_traits/add_reference.hpp>
#include <boost/mpl/bool.hpp>
namespace proto = boost::proto;
using proto::_;
using boost::mpl::true_;
using boost::mpl::false_;
struct map_list_of_tag
{};
// A simple callable function object that inserts a
// (key,value) pair into a map.
struct insert
: proto::callable
{
template<typename Sig>
struct result;
template<typename This, typename Map, typename Key, typename Value>
struct result<This(Map, Key, Value)>
: boost::add_reference<Map>
{};
template<typename Map, typename Key, typename Value>
Map &operator()(Map &map, Key const &key, Value const &value) const
{
map.insert(typename Map::value_type(key, value));
return map;
}
};
struct nest
: proto::callable
{
template<typename Sig>
struct result;
template<typename This, typename Map, typename Key, typename Value>
struct result<This(Map, Key, Value)>
: boost::add_reference<Map>
{};
template<typename Map, typename Key, typename Value>
Map& operator()(Map& map, Key const &key, Value const &value) const
{
map[key]=Map();//assign a map to the variant at key
map[key]=value;//assign a map_list_of_expr to a map
return boost::get<Map&>(map[key]); //proto seems to need that this return value be a reference
}
};
// Work-arounds for Microsoft Visual C++ 7.1
#if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
#define MapListOf(x) proto::call<MapListOf(x)>
#define _value(x) call<proto::_value(x)>
#endif
// The grammar for valid map-list expressions, and a
// transform that populates the map.
struct MapListOf
: proto::or_<
proto::when<
// map_list_of(a,b)
proto::function<
proto::terminal<map_list_of_tag>
, proto::terminal<_>
, proto::terminal<_>
>
, insert(
proto::_data //the map you are assigning to
, proto::_value(proto::_child1) //a char const [N] key
, proto::_value(proto::_child2) //a char const [N] value
)
>
, proto::when<
// map_list_of(a,map)
proto::function<
proto::terminal<map_list_of_tag>
, proto::terminal<_>
, MapListOf
>
, insert(
proto::_data //the map you are assigning to
, proto::_value(proto::_child1)
, nest(
proto::_data, //the map you are assigning to
proto::_value(proto::_child1), //a char const [N] key
proto::_child2 //a proto expression (map_list_of_expr) that represents a nested map
)
)
>
, proto::when<
// map_list_of(a,b)(c,d)...
proto::function<
MapListOf
, proto::terminal<_>
, proto::terminal<_>
>
, insert(
MapListOf(proto::_child0) //evaluates the transform recursively
, proto::_value(proto::_child1) //a char const [N] key
, proto::_value(proto::_child2) //a char const [N] value
)
>
, proto::when<
// map_list_of(a,b)(c,map)...
proto::function<
MapListOf
, proto::terminal<_>
, MapListOf
>
, insert(
MapListOf(proto::_child0) //evaluates the transform recursively
, proto::_value(proto::_child1) //a char const [N] key
, nest(
proto::_data, //the map you are assigning to
proto::_value(proto::_child1), //a char const [N] key
proto::_child2 //a proto expression that represents a nested map
)
)
>
>
{};
#if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
#undef MapListOf
#undef _value
#endif
template<typename Expr>
struct map_list_of_expr;
struct map_list_of_dom
: proto::domain<proto::pod_generator<map_list_of_expr>, MapListOf>
{};
// An expression wrapper that provides a conversion to a
// map that uses the MapListOf
template<typename Expr>
struct map_list_of_expr
{
BOOST_PROTO_BASIC_EXTENDS(Expr, map_list_of_expr, map_list_of_dom)
BOOST_PROTO_EXTENDS_FUNCTION()
template<typename Key, typename Value, typename Cmp, typename Al>
operator std::map<Key, Value, Cmp, Al> () const
{
BOOST_MPL_ASSERT((proto::matches<Expr, MapListOf>));
std::map<Key, Value, Cmp, Al> map;
return MapListOf()(*this, 0, map);
}
};
map_list_of_expr<proto::terminal<map_list_of_tag>::type> const map_list_of = {{{}}};
typedef boost::make_recursive_variant<std::string, std::map<std::string, boost::recursive_variant_> >::type Value;
typedef std::map<std::string, Value> Map;
struct printer : boost::static_visitor<void>
{
printer(int indent) :indent(indent) {}
void operator()(const std::string& val) const
{
std::cout << std::string(indent, ' ') << val << std::endl;
}
void operator()(const Map& val) const
{
for (Map::const_iterator it = val.begin(), end = val.end(); it != end; ++it)
{
std::cout << std::string(indent, ' ') << it->first << std::endl;
boost::apply_visitor(printer(indent + 4), it->second);
std::cout << std::string(indent, ' ') << std::endl;
}
}
int indent;
};
void print(const Value& val)
{
boost::apply_visitor(printer(0), val);
}
int main()
{
// Initialize a map:
Map json =
map_list_of
("foo", "FOO")
("fuu", "FUU")
("bar",
map_list_of
("no", "abc")
("yes", "ABC")
)
("baa",
map_list_of
("no", "xyz")
("yes", "XYZ")
)
;
print(json);
return 0;
}