gpt4 book ai didi

c++ - 使用 C++ 和 BOOST 读取 JSON 文件

转载 作者:搜寻专家 更新时间:2023-10-31 01:02:11 26 4
gpt4 key购买 nike

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\/"
}
]
}

我想将这个“远程文件夹树”与本地文件夹树(例如包含本地文件位置的字符串 vector )进行比较,所以我想在(字符串, 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

我想知道是否可以用这个结果生成 map<string, vector <map<string,string> > > ,它将有 2 个键:“文件夹”和"file",通过这 2 个键,我们可以访问包含每个对象(文件或文件夹)信息的映射类型 vector 。如果这是可行的,它将降低任务的复杂性(比较两个文件夹列表)

示例:T["folder"][0]["id"] 将返回“109”; T["files"][0]["name"] 会返回 "logo.png"

更新:这个问题很老,但我想给出一个建议:只要你想在 C++ 下处理 Json,就使用 RAPIDJSON。

最佳答案

因为其他答案中的数据结构was deemed "very complex"目标数据结构是suggested to be :

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”到该数据结构的转换(参见其他答案: Reading JSON file with C++ and BOOST )。

但是,如果我们“跳过中间人”并将 JSON 专门解析为显示的 Data 结构,OP 可能会更高兴。这“简化”了语法,使其仅针对此类文档:

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_('}') | ',';

这个语法允许

  • 无意义的空格,
  • 对象内属性的重新排序
  • 和省略的对象属性

好处:

  • 早期检查属性值类型
  • 缩短编译时间
  • 确实减少了代码:减少了 37 个 LoC(不包括约 22% 的示例 JSON 行)

That last benefit has a flip side: if ever you want to read slightly different JSON, now you need to muck with the grammar instead of just writing a different extraction/transform. At 37 lines of code, my preference is with the other answer but I'll leave it to you to decide.

下面是直接使用该语法的同一个演示程序:

Live On Coliru

//#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

关于c++ - 使用 C++ 和 BOOST 读取 JSON 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27746109/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com