(问题从Spirit-general mailing list解除)
您好,
我在精神qi的解析器中工作。语法运行良好,但是我有一些问题用Semantic Actions填充我的struct实例。
使用直接结构属性,例如" Request.id"和#34; Request.url",代码正在运行。但我不知道如何填充嵌套结构中的属性" Info",如何在" Request.list"中推送值。
这是我的代码(要解析的字符串可以包含任何顺序的值):
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <vector>
#include <string>
struct Request
{
std::string id;
std::string url;
std::vector<std::string> list;
struct Info
{
std::string id;
std::string desc;
};
Info info;
};
BOOST_FUSION_ADAPT_STRUCT(
Request,
(std::string, id)
)
template <typename Iterator>
struct request_parser : boost::spirit::qi::grammar<Iterator, Request(), boost::spirit::ascii::space_type>
{
request_parser() : request_parser::base_type(start)
{
using namespace boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phx = boost::phoenix;
quoted_string %= lexeme['"' >> +(char_ - '"') >> '"'];
start %=
BR_OP
>>((DQUOTE >> lit(INFO) >> DQUOTE >> COLON
>> BR_OP
>> ((DQUOTE >> lit(DESC) >> DQUOTE >> COLON >> quoted_string)
^ (DQUOTE >> lit(ID) >> DQUOTE >> COLON >> quoted_string)) % COMMA
>> BR_CL)
^(DQUOTE >> lit(ID) >> DQUOTE >> COLON >> quoted_string[phx::bind(&Request::id, _val) = _1])
^(DQUOTE >> lit(URL) >> DQUOTE >> COLON >> quoted_string[phx::bind(&Request::url, _val) = _1])
^(DQUOTE >> lit(LIST) >> DQUOTE >> COLON
>> SQ_OP
>> quoted_string % COMMA
>> SQ_CL)) % COMMA
>>
BR_CL
;
}
boost::spirit::qi::rule<Iterator, std::string(), boost::spirit::ascii::space_type> quoted_string;
boost::spirit::qi::rule<Iterator, Request(), boost::spirit::ascii::space_type> start;
char BR_OP = '{';
char BR_CL = '}';
char DQUOTE = '"';
char COLON = ':';
char SQ_OP = '[';
char SQ_CL = ']';
char COMMA = ',';
const char* LIST = "list";
const char* ID = "id";
const char* URL = "url";
const char* INFO = "info";
const char* DESC = "desc";
};
int main()
{
typedef std::string::iterator iterator_type;
typedef request_parser<iterator_type> requester_parser;
std::string str = "{\"list\":[\"data1\",\"data2\"],\"info\":{\"desc\":\"description\",\"id\":\"23\"},\"id\":\"1234\",\"url\":\"ok.com\"}";
Request rqst;
requester_parser parser;
using boost::spirit::ascii::space;
boost::spirit::qi::phrase_parse(str.begin(), str.end(), parser, space, rqst);
using std::cout;
using std::endl;
cout << rqst.id << endl;
cout << rqst.url << endl;
cout << rqst.list.size() << endl;
cout << rqst.info.id << endl;
cout << rqst.info.desc << endl;
}
谢谢! 埃米利亚诺
答案 0 :(得分:5)
与往常一样,我将建议解决方案不使用语义操作(Boost Spirit: "Semantic actions are evil"?)。我真的相信语义行为应该谨慎使用,因为它们大多有可能使事情复杂化。 (例如new
和delete
,您应该使用它们的时间非常有限。)
在这种情况下,你几乎就在那里:自动属性传播合成a tuple of permutation members in sequence。
所以,如果你只是按语法顺序调整你的结构,你会没事的:
BOOST_FUSION_ADAPT_STRUCT(Request::Info, id, desc)
BOOST_FUSION_ADAPT_STRUCT(Request, info, id, url, list)
语法本身存在一些问题((a ^ b) % ','
与(a | b) % ','
不相似:它会解析a
,b
,ab
,{{ 1}},ba
等。)。
我将规则拆分了一点以删除冗余并使用“智能”分隔符来指望在正确的位置使用逗号:
a,ba,b
完整的语法变为:
delim_ = &lit('}') | ','; // unless at end of block, expect a comma
注意
string_ = '"' >> *~char_('"') >> '"';
key_ = '"' >> string(_r1) >> '"';
prop_ = key_(_r1) >> ':' >> string_ >> delim_;
info_ = key_("info"s) >> ':'
>> '{'
>> (prop_("desc"s) ^ prop_("id"s))
>> '}'
>> delim_
;
list_ = key_("list"s) >> ':'
>> '[' >> string_ % ',' >> ']'
>> delim_
;
request_ = '{' >> (info_ ^ prop_("id"s) ^ prop_("url"s) ^ list_) >> '}';
和key_
。您可以使用Nabialek技巧进一步推广非字符串属性prop_
<强> Live On Coliru 强>
start = skip(space) [ request_ ];
打印:
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <vector>
#include <string>
using namespace std::literals::string_literals;
struct Request
{
std::string id;
std::string url;
std::vector<std::string> list;
struct Info {
std::string id;
std::string desc;
};
Info info;
};
BOOST_FUSION_ADAPT_STRUCT(Request::Info, id, desc)
BOOST_FUSION_ADAPT_STRUCT(Request, info, id, url, list)
template <typename Iterator>
struct request_parser : boost::spirit::qi::grammar<Iterator, Request()>
{
request_parser() : request_parser::base_type(start)
{
using namespace boost::spirit::qi;
delim_ = &lit('}') | ',';
string_ = '"' >> *~char_('"') >> '"';
key_ = '"' >> string(_r1) >> '"';
prop_ = key_(_r1) >> ':' >> string_ >> delim_;
info_ = key_("info"s) >> ':'
>> '{'
>> (prop_("desc"s) ^ prop_("id"s))
>> '}'
>> delim_
;
list_ = key_("list"s) >> ':'
>> '[' >> string_ % ',' >> ']'
>> delim_
;
request_ = '{' >> (info_ ^ prop_("id"s) ^ prop_("url"s) ^ list_) >> '}';
start = skip(space) [ request_ ];
}
private:
using Skipper = boost::spirit::qi::space_type;
boost::spirit::qi::rule<Iterator, Request()> start;
boost::spirit::qi::rule<Iterator, Request(), Skipper> request_;
boost::spirit::qi::rule<Iterator, Request::Info(), Skipper> info_;
boost::spirit::qi::rule<Iterator, std::vector<std::string>(), Skipper> list_;
boost::spirit::qi::rule<Iterator, std::string(std::string), Skipper> prop_;
// lexemes
boost::spirit::qi::rule<Iterator, std::string()> string_;
// literals
boost::spirit::qi::rule<Iterator, void(std::string)> key_;
boost::spirit::qi::rule<Iterator> delim_;
};
int main()
{
typedef std::string::iterator iterator_type;
typedef request_parser<iterator_type> requester_parser;
std::string str = R"({
"list": ["data1", "data2"],
"info": {
"desc": "description",
"id": "23"
},
"id": "1234",
"url": "ok.com"
})";
Request parsed;
requester_parser parser;
parse(str.begin(), str.end(), parser, parsed);
std::cout << "parsed.id: " << parsed.id << "\n";
std::cout << "parsed.url: " << parsed.url << "\n";
std::cout << "parsed.list.size(): " << parsed.list.size() << "\n";
std::cout << "parsed.info.id: " << parsed.info.id << "\n";
std::cout << "parsed.info.desc: " << parsed.info.desc << "\n";
}