boost::property_tree::ptree обращаясь к первому сложному элементу массива
Мой JSON это:
{
"apps":[
{
"id":"x",
"val":"y",
}
]
}
я могу получить id
значение "х", циклически, и выход, когда it.first
является id
:
for (const ptree::value_type &app : root.get_child("apps"))
{
for (const ptree::value_type &it : app.second) {
if (it.first == "id") {
std::cout << it.second.get_value<std::string>().c_str() << std::endl;
}
}
}
Однако я хочу получить id
значение примерно так:
std::cout << root.get<std::string>("apps[0].id").c_str() << std::endl;
Конечно, это ничего не показывает для меня, потому что я, вероятно, использую неправильный синтаксис для доступа к 1-му элементу массива приложений. Возможно, все это нужно сделать по-другому.
Я нашел только этот уродливый и опасный метод:
std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;
Я не могу использовать его таким образом, так как он не выдаст исключение, когда массив пуст, это дамп памяти!
Ниже приведена вся программа, чтобы облегчить задачу любому, кто хочет помочь:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
int main()
{
std::stringstream ss("{\"apps\":[{\"id\":\"x\",\"val\":\"y\"}]}");
ptree root;
read_json(ss, root);
for (const ptree::value_type &app : root.get_child("apps"))
{
for (const ptree::value_type &it : app.second) {
if (it.first == "id") {
std::cout << it.second.get_value<std::string>().c_str() << std::endl;
}
}
}
std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;
return 0;
}
1 ответ
Как говорят документы, элементы массива являются узлами с ""
ключи.
Если вы после первого элемента, вам повезло:
root.get("apps..id", "")
..
в пути выбирается первый пустой ключ
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::stringstream ss(R"({"apps":[{"id":"x","val":"y"}]})");
ptree root;
read_json(ss, root);
std::cout << root.get("apps..id", "") << "\n";
}
БОНУС
Если вам нужно обратиться к элементам, отличным от первого, напишите вспомогательную функцию. Это было бы хорошим началом:
#include <string>
#include <stdexcept> // std::out_of_range
template <typename Tree>
Tree query(Tree& pt, typename Tree::path_type path) {
if (path.empty())
return pt;
auto const head = path.reduce();
auto subscript = head.find('[');
auto name = head.substr(0, subscript);
auto index = std::string::npos != subscript && head.back() == ']'
? std::stoul(head.substr(subscript+1))
: 0u;
auto matches = pt.equal_range(name);
if (matches.first==matches.second)
throw std::out_of_range("name:" + name);
for (; matches.first != matches.second && index; --index)
++matches.first;
if (index || matches.first==matches.second)
throw std::out_of_range("index:" + head);
return query(matches.first->second, path);
}
Вот некоторые живые тесты, использующие это:
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::stringstream ss(R"({
"apps": [
{
"id": "x",
"val": "y",
"id": "hidden duplicate"
},
{
"id": "a",
"val": "b"
}
]
})");
ptree root;
read_json(ss, root);
for (auto path : {
"apps..id", "apps.[0].id", // (equivalent)
//
"apps.[0].id[]", // invalid
"apps.[0].id[0]", // x
"apps.[0].id[1]", // hidden duplicate
"apps.[1].id", // a
"apps.[1].id[0]", // a
"apps.[1].id[1]", // out of range
"apps.[2].id", // out of range
"drinks" // huh, no drinks at the foo bar
}) try {
std::cout << "Path '" << path << "' -> ";
std::cout << query(root, path).get_value<std::string>() << "\n";
} catch(std::exception const& e) {
std::cout << "Error: " << e.what() << "\n";
}
}
Печать:
Path 'apps..id' -> x
Path 'apps.[0].id' -> x
Path 'apps.[0].id[]' -> Error: stoul
Path 'apps.[0].id[0]' -> x
Path 'apps.[0].id[1]' -> hidden duplicate
Path 'apps.[1].id' -> a
Path 'apps.[1].id[0]' -> a
Path 'apps.[1].id[1]' -> Error: index:id[1]
Path 'apps.[2].id' -> Error: index:[2]
Path 'drinks' -> Error: name:drinks