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
Другие вопросы по тегам