我无法使用 C++ boost property_tree 将嵌套键解析为 JSON 字符串

I can't with C++ boost property_tree parse nested key into JSON string

使用 JSON 字符串,我可以在没有任何嵌套键的情况下从 JSON 获取键值:

std::string getFieldFromJson(std::string json, std::string field)
{
    std::stringstream jsonEncoded(json); // string to stream
    boost::property_tree::ptree root;
    boost::property_tree::read_json(jsonEncoded, root);
 
    if (root.empty())
        return "";
 
    return root.get <std::string>(field);
}

但是如果我有一个像这样的 JSON 我就做不到:

    {
    "user": {
        "id": 1,
        "created_at": "2017-08-16T09:23:48.525+02:00",
      ......
    }

比如我想读取键“created_at”的值。

使用提升 JSON:

Live On Coliru

#include <boost/algorithm/string.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header-only/COLIRU
#include <iostream>
namespace json = boost::json;

static std::string getFieldFromJson(std::string_view json,
                                    std::string_view field) {
    std::vector<std::string_view> tokens;
    boost::algorithm::split(tokens, field, boost::algorithm::is_any_of("."));

    auto el = boost::json::parse({json.data(), json.size()});
    for (std::string_view token : tokens)
        el = el.at({token.data(), token.size()});

    return value_to<std::string>(el);
}

int main() {
    auto input = R"(
        {
            "user": {
                "id": 1,
                "created_at": "2017-08-16T09:23:48.525+02:00",
                "name": "John Doe"
            }
        })";

    std::cout << getFieldFromJson(input, "user.created_at") << std::endl;
}

如果有任何问题wrong/isn未找到

,它会抛出异常

改进界面

与其每次都解析,不如解析一次:

Live On Coliru

#include <boost/algorithm/string.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header-only/COLIRU
#include <iostream>
namespace json = boost::json;

static json::value const& property(json::value const& root,
                                   std::string_view   expr) {
    std::vector<std::string_view> tokens;
    boost::algorithm::split(tokens, expr, boost::algorithm::is_any_of("."));

    auto* cursor = &root;
    for (std::string_view token : tokens)
        cursor = &(cursor->at({token.data(), token.size()}));

    return *cursor;
}

int main() {
    auto doc = json::parse(R"( {
            "user": {
                "id": 1,
                "created_at": "2017-08-16T09:23:48.525+02:00",
                "name": "John Doe"
            }
        })");

    std::cout << property(doc, "user.created_at") << std::endl;
}

版画

"2017-08-16T09:23:48.525+02:00"

更好:映射您的类型

如果您处理丢失的数据,您可以创建映射器,这样您就不必一直手动强制转换数据类型:

Live On Coliru

#include <boost/algorithm/string.hpp>
#include <boost/date_time/local_time/local_time_io.hpp>
#include <boost/date_time/local_time/conversion.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for header-only/COLIRU
#include <iostream>
namespace json = boost::json;
using boost::local_time::local_date_time;

boost::local_time::time_zone_ptr
    tz(new boost::local_time::posix_time_zone("MST-07:00:00"));

namespace boost::local_time {
    static local_date_time tag_invoke(json::value_to_tag<local_date_time>, json::value const& v) {
        std::stringstream iss;
        iss.exceptions(std::ios::failbit | std::ios::badbit);
        {
            auto& s = v.as_string();
            std::cout << "Trying: " << s << std::endl;
            iss.write(s.data(), s.size());
        }
        local_date_time ldt(boost::date_time::not_a_date_time, tz);
        iss.imbue(std::locale(
            iss.getloc(), new local_time_input_facet("%Y-%m-%dT%H:%M:%S%Q")));
        iss >> ldt;
        return ldt;
    }
}

struct User {
    int64_t         id;
    local_date_time createdAt;
    std::string     name;

    friend User tag_invoke(json::value_to_tag<User>, json::value const& v) {
        auto& user = v.at("user");
        std::cout << user << std::endl;
        return {
            user.at("id").as_int64(),
            value_to<local_date_time>(user.at("created_at")),
            value_to<std::string>(user.at("name")),
        };
    }
};

using Users = std::vector<User>;

int main() {
    auto doc = json::parse(R"(
[
    {
        "user": {
            "id": 1,
            "created_at": "2017-08-16T09:23:48.525+02:00",
            "name": "John Doe"
        }
    },
    {
        "user": {
            "id": 2,
            "created_at": "2022-04-23T14:56:16+00:00",
            "name": "Jane Doe"
        }
    }
]
)");

    auto users = value_to<Users>(doc);
    auto now   = boost::local_time::local_sec_clock::local_time(tz);

    for (auto& [id, createdAt, name] : users) {
        std::cout << "user #" << id << " " << std::quoted(name)
                  << " was created " << (now - createdAt).total_seconds()
                  << " seconds ago\n";
    }
}

打印例如

{"id":1,"created_at":"2017-08-16T09:23:48.525+02:00","name":"John Doe"}
Trying: "2017-08-16T09:23:48.525+02:00"
{"id":2,"created_at":"2022-04-23T14:56:16+00:00","name":"Jane Doe"}
Trying: "2022-04-23T14:56:16+00:00"
user #1 "John Doe" was created 147853388 seconds ago
user #2 "Jane Doe" was created 3040 seconds ago

谢谢。我也用下一个解决了它:

std::string getFieldFromJson(std::string json, std::string field)
{
    std::stringstream jsonEncoded(json); // string to stream convertion
    boost::property_tree::ptree root;
    boost::property_tree::read_json(jsonEncoded, root);

    return (root.get<std::string>("user." + field));
}