HTTP服务器向我发送JSON响应(字符串),如下所示:
{ "folders" : [{ "id" : 109, "parent_id" : 110, "path" : "\/1\/105\/110\/" }, { "id" : 110, "parent_id" : 105, "path" : "\/1\/105\/" } ], "files" : [{ "id" : 26, "parent_id" : 105, "name" : "picture.png", "hash" : "md5_hash", "path" : "\/1\/105\/" }, { "id" : 25, "parent_id" : 110, "name" : "another_picture.jpg", "hash" : "md5_hash", "path" : "\/1\/105\/110\/" } ] }
我想将此“远程文件夹的树”与本地文件夹树(例如,包含本地文件位置的字符串向量)进行比较,因此我想在(string,vector(map(string ,string)))(我不知道是否可行)。
我正在开发一种工具来在本地文件夹和远程文件夹之间同步文件,因此我正在使用boost列出本地文件夹,并且我想将本地列表与远程列表(JSON响应)进行比较以生成操作(下载本地文件夹中不存在的丢失文件,上传远程文件夹中不存在的文件)。
有了这个功能,我在另一个问题上发现:
void print(boost::property_tree::ptree const& pt) { using boost::property_tree::ptree; ptree::const_iterator end = pt.end(); for (ptree::const_iterator it = pt.begin(); it != end; ++it) { std::cout << it->first << ": " << it->second.get_value<std::string>() << std::endl; print(it->second); } }
我成功打印了以下内容:
folders: : id: 109 parent_id: 110 name: 2011_pictures : id: 110 parent_id: 105 name: Aminos files: id: 26 parent_id: 105 name: logo.png : id: 5 parent_id: 109 name: me.jpg
我想知道是否有可能生成此结果a `map<string, vector >
`,它将具有2个键:“文件夹”和“文件”,并且使用这2个键,我们可以访问包含每个对象(文件或文件)信息的map类型的向量夹)。如果可行,它将降低任务的复杂性(比较两个文件夹列表)
示例:T [“ folder”] [0] [“ id”]将返回“ 109”; T [“ files”] [0] [“ name”]将返回“ logo.png”
更新:这个问题很旧,但是我想提个建议:每当您想在C ++下处理Json时,请使用RAPIDJSON。
由于另一个答案中的数据结构被认为“非常复杂”,因此建议目标数据结构为:
struct Data { struct Folder { int id, parent_id; std::string path; }; struct File { int id, parent_id; std::string path, name, md5_hash; }; using Folders = std::vector<Folder>; using Files = std::vector<File>; Folders folders; Files files; };
但是,如果我们“跳过中间人”并将JSON专门解析为所示Data结构,则OP可能会更满意。这“简化”了语法,使其仅针对此类文档:
Data
start = '{' >> (folders_ >> commasep) ^ (files_ >> commasep) >> '}'; folders_ = prop_key(+"folders") >> '[' >> -(folder_ % ',') >> ']'; files_ = prop_key(+"files") >> '[' >> -(file_ % ',') >> ']'; folder_ = '{' >> ( (prop_key(+"id") >> int_ >> commasep) ^ (prop_key(+"parent_id") >> int_ >> commasep) ^ (prop_key(+"path") >> text_ >> commasep) ) >> '}'; file_ = '{' >> ( (prop_key(+"id") >> int_ >> commasep) ^ (prop_key(+"parent_id") >> int_ >> commasep) ^ (prop_key(+"path") >> text_ >> commasep) ^ (prop_key(+"name") >> text_ >> commasep) ^ (prop_key(+"hash") >> text_ >> commasep) ) >> '}'; prop_key = lexeme ['"' >> lazy(_r1) >> '"'] >> ':'; commasep = &char_('}') | ',';
这个语法允许
优点:
最后一个好处是反面:如果您想读取稍有不同的JSON,现在您需要处理语法,而不仅仅是编写其他提取/转换。 在37行代码中,我偏爱其他答案,但我将由您自己决定。
这是直接使用此语法的相同演示程序:
[Live On Coliru](http://coliru.stacked-crooked.com/a/cbbe576146a96f58)
//#define BOOST_SPIRIT_DEBUG #include <boost/fusion/adapted.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> namespace qi = boost::spirit::qi; static std::string const sample = R"( { "folders" : [{ "id" : 109, "parent_id" : 110, "path" : "\/1\/105\/110\/" }, { "id" : 110, "parent_id" : 105, "path" : "\/1\/105\/" } ], "files" : [{ "id" : 26, "parent_id" : 105, "name" : "picture.png", "hash" : "md5_hash", "path" : "\/1\/105\/" }, { "id" : 25, "parent_id" : 110, "name" : "another_picture.jpg", "hash" : "md5_hash", "path" : "\/1\/105\/110\/" } ] })"; struct Data { struct Folder { int id, parent_id; std::string path; }; struct File { int id, parent_id; std::string path, name, md5_hash; }; using Folders = std::vector<Folder>; using Files = std::vector<File>; Folders folders; Files files; }; BOOST_FUSION_ADAPT_STRUCT(Data::Folder, (int,id)(int,parent_id)(std::string,path)) BOOST_FUSION_ADAPT_STRUCT(Data::File, (int,id)(int,parent_id)(std::string,path)(std::string,name)(std::string,md5_hash)) BOOST_FUSION_ADAPT_STRUCT(Data, (Data::Folders,folders)(Data::Files,files)) namespace folder_info { // adhoc JSON parser template <typename It, typename Skipper = qi::space_type> struct grammar : qi::grammar<It, Data(), Skipper> { grammar() : grammar::base_type(start) { using namespace qi; start = '{' >> (folders_ >> commasep) ^ (files_ >> commasep) >> '}'; folders_ = prop_key(+"folders") >> '[' >> -(folder_ % ',') >> ']'; files_ = prop_key(+"files") >> '[' >> -(file_ % ',') >> ']'; folder_ = '{' >> ( (prop_key(+"id") >> int_ >> commasep) ^ (prop_key(+"parent_id") >> int_ >> commasep) ^ (prop_key(+"path") >> text_ >> commasep) ) >> '}'; file_ = '{' >> ( (prop_key(+"id") >> int_ >> commasep) ^ (prop_key(+"parent_id") >> int_ >> commasep) ^ (prop_key(+"path") >> text_ >> commasep) ^ (prop_key(+"name") >> text_ >> commasep) ^ (prop_key(+"hash") >> text_ >> commasep) ) >> '}'; prop_key = lexeme ['"' >> lazy(_r1) >> '"'] >> ':'; commasep = &char_('}') | ','; //////////////////////////////////////// // Bonus: properly decoding the string: text_ = '"' >> *ch_ >> '"'; ch_ = +( ~char_("\"\\")) [ _val += _1 ] | qi::lit("\x5C") >> ( // \ (reverse solidus) qi::lit("\x22") [ _val += '"' ] | // " quotation mark U+0022 qi::lit("\x5C") [ _val += '\\' ] | // \ reverse solidus U+005C qi::lit("\x2F") [ _val += '/' ] | // / solidus U+002F qi::lit("\x62") [ _val += '\b' ] | // b backspace U+0008 qi::lit("\x66") [ _val += '\f' ] | // f form feed U+000C qi::lit("\x6E") [ _val += '\n' ] | // n line feed U+000A qi::lit("\x72") [ _val += '\r' ] | // r carriage return U+000D qi::lit("\x74") [ _val += '\t' ] | // t tab U+0009 qi::lit("\x75") // uXXXX U+XXXX >> _4HEXDIG [ append_utf8(qi::_val, qi::_1) ] ); BOOST_SPIRIT_DEBUG_NODES((files_)(folders_)(file_)(folder_)(start)(text_)) } private: qi::rule<It, Data(), Skipper> start; qi::rule<It, Data::Files(), Skipper> files_; qi::rule<It, Data::Folders(), Skipper> folders_; qi::rule<It, Data::File(), Skipper> file_; qi::rule<It, Data::Folder(), Skipper> folder_; qi::rule<It, void(const char*), Skipper> prop_key; qi::rule<It, std::string()> text_, ch_; qi::rule<It> commasep; struct append_utf8_f { template <typename...> struct result { typedef void type; }; template <typename String, typename Codepoint> void operator()(String& to, Codepoint codepoint) const { auto out = std::back_inserter(to); boost::utf8_output_iterator<decltype(out)> convert(out); *convert++ = codepoint; } }; boost::phoenix::function<append_utf8_f> append_utf8; qi::uint_parser<uint32_t, 16, 4, 4> _4HEXDIG; }; template <typename Range, typename It = typename boost::range_iterator<Range const>::type> Data parse(Range const& input) { grammar<It> g; It first(boost::begin(input)), last(boost::end(input)); Data parsed; bool ok = qi::phrase_parse(first, last, g, qi::space, parsed); if (ok && (first == last)) return parsed; throw std::runtime_error("Remaining unparsed: '" + std::string(first, last) + "'"); } } int main() { auto parsed = folder_info::parse(sample); for (auto& e : parsed.folders) std::cout << "folder:\t" << e.id << "\t" << e.path << "\n"; for (auto& e : parsed.files) std::cout << "file:\t" << e.id << "\t" << e.path << "\t" << e.name << "\n"; }
输出:
folder: 109 /1/105/110/ folder: 110 /1/105/ file: 26 /1/105/ picture.png file: 25 /1/105/110/ another_picture.jpg