如何用boost::property_tree将std::unordered_map转为JSON数组?
问题:将std::unordered_map转换为指定JSON数组格式
给定如下C++ std::unordered_map数据:
std::unordered_map<std::string, std::vector<std::string>> data = { {"name", {"a", "b", "c", "d", "e"}}, {"price", {"1.1", "2.2", "3.3", "4.4", "5.5"}}, {"id", {"1", "2", "3", "4", "5"}} };
需要转换为如下结构的JSON数组:
[ { "id": "1", "price": "1.1", "name": "a" }, { "id": "2", "price": "2.2", "name": "b" }, { "id": "3", "price": "3.3", "name": "c" }, { "id": "4", "price": "4.4", "name": "d" }, { "id": "5", "price": "5.5", "name": "e" } ]
尝试使用boost::property_tree::ptree和write_json函数实现,代码如下:
size_t nrows = 5; boost::property_tree::ptree pt; for(size_t i = 0; i < nrows; i++) { boost::property_tree::ptree body_node; for(const auto& kv : data) { body_node.put(kv.first, kv.second[i]); } pt.push_back(std::make_pair("", body_node)); } boost::property_tree::write_json(std::cout, pt);
但得到的输出是带空键的JSON对象,而非预期数组:
{ "": { "id": "1", "price": "1.1", "name": "a" }, "": { "id": "2", "price": "2.2", "name": "b" }, "": { "id": "3", "price": "3.3", "name": "c" }, "": { "id": "4", "price": "4.4", "name": "d" }, "": { "id": "5", "price": "5.5", "name": "e" } }
解决方案
原因分析
boost::property_tree::ptree的JSON实现存在局限性:它默认将根节点的空键子节点渲染为JSON对象的属性,而JSON对象不允许重复键,因此会出现上述错误输出。同时,ptree原生不支持直接输出顶层JSON数组,因为其根节点被设计为对象结构。
方案1:使用Boost.JSON库(推荐)
如果你的Boost版本≥1.75.0,建议使用Boost.JSON库,它原生支持JSON数组和对象,更贴合需求,代码更简洁:
#include <boost/json.hpp> #include <iostream> #include <unordered_map> #include <vector> namespace json = boost::json; int main() { std::unordered_map<std::string, std::vector<std::string>> data = { {"name", {"a", "b", "c", "d", "e"}}, {"price", {"1.1", "2.2", "3.3", "4.4", "5.5"}}, {"id", {"1", "2", "3", "4", "5"}} }; json::array arr; const size_t nrows = data.begin()->second.size(); for (size_t i = 0; i < nrows; ++i) { json::object obj; for (const auto& kv : data) { obj[kv.first] = kv.second[i]; } arr.push_back(obj); } std::cout << json::serialize(arr) << std::endl; return 0; }
这段代码会直接输出符合预期的顶层JSON数组。
方案2:基于Boost.PropertyTree的兼容处理
如果必须使用property_tree,可以通过手动处理输出的方式绕过顶层数组的限制:
- 先将数组放在一个临时键的子节点中
- 输出后手动去除外层的对象包裹
修改后的代码:
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/json_parser.hpp> #include <iostream> #include <unordered_map> #include <vector> #include <sstream> namespace pt = boost::property_tree; int main() { std::unordered_map<std::string, std::vector<std::string>> data = { {"name", {"a", "b", "c", "d", "e"}}, {"price", {"1.1", "2.2", "3.3", "4.4", "5.5"}}, {"id", {"1", "2", "3", "4", "5"}} }; const size_t nrows = 5; pt::ptree root_pt; pt::ptree array_pt; for (size_t i = 0; i < nrows; ++i) { pt::ptree body_node; for (const auto& kv : data) { body_node.put(kv.first, kv.second[i]); } array_pt.push_back(std::make_pair("", body_node)); } root_pt.add_child("temp", array_pt); // 将输出写入字符串流 std::ostringstream oss; pt::write_json(oss, root_pt); // 手动去除外层的 {"temp": ...} 包裹 std::string json_str = oss.str(); size_t start = json_str.find('['); size_t end = json_str.rfind(']'); if (start != std::string::npos && end != std::string::npos) { std::cout << json_str.substr(start, end - start + 1) << std::endl; } return 0; }
此方法通过字符串截取得到纯净的JSON数组输出,适合无法升级Boost版本的场景。
内容的提问来源于stack exchange,提问作者rufous
相关产品推荐
相关产品推荐

