简体   繁体   English

boost :: property_tree :: ptree访问数组的第一个复杂元素

[英]boost::property_tree::ptree accessing array's first complex element

My JSON is this: 我的JSON是这样的:

{
    "apps":[
        {
            "id":"x",
            "val":"y",
        }
    ]
}

I can get id 's value "x" by looping, and exiting when it.first is id : 我可以通过循环并在it.firstid时退出来获得id的值“ x”:

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;
        }
    }
}

What I want, however, is to get the id 's value by something like this: 但是,我想要通过以下方式获取id的值:

std::cout << root.get<std::string>("apps[0].id").c_str() << std::endl;

Of course this displays nothing to me, for I am probably using wrong syntax accessing 1st element of the apps array. 当然,这对我来说什么也没有显示,因为我可能使用错误的语法访问apps数组的第一个元素。 It might be that this has to be done in a different way all together. 可能这必须以不同的方式一起完成。

I have found only this ugly and dangerous method: 我只发现了这种丑陋而危险的方法:

std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;

I cannot really use it this way as it won't throw an exception when the array is empty, it will core dump! 我不能真正以这种方式使用它,因为当数组为空时它不会引发异常,它将核心转储!

Below is the whole program to make it easier for any one who wants to help: 以下是整个程序,可让任何想要帮助的人都更轻松:

#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;
}

As the docs say, array elements are nodes with "" keys. 正如文档所说,数组元素是带有""键的节点。

If you're after the first element, you're in luck: 如果您在第一个元素之后,那么您很幸运:

root.get("apps..id", "")

The .. in the path selects the first empty key 路径中的..选择第一个空键

Live On Coliru 生活在Coliru

#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";
}

BONUS 奖金

If you need to address elements other than the first, write a helper function. 如果您需要处理除第一个元素以外的其他元素,请编写一个辅助函数。 This would be a good start: 这将是一个好的开始:

#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);
}

Here's some live tests using it: 这是一些使用它的实时测试:

Live On Coliru 生活在Coliru

#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";
    }
}

Prints: 打印:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM