通过多级提升树迭代 [英] Iterate through multilevel boost tree
问题描述
我的树看起来像这样:
{
"Library":
{
"L_ID": "1",
"Book":
{
"B_ID": "1",
"Title": "Moby Dick"
},
"Book":
{
"B_ID": "2",
"Title": "Jurassic Park"
}
},
"Library":
{
"L_ID": "2",
"Book":
{
"B_ID": "1",
"Title": "Velocity"
},
"Book":
{
"B_ID": "2",
"Title": "Creeper"
}
}
}
我是什么我希望做的是遍历库。当我找到我正在寻找的L_ID时,遍历书籍直到找到我正在寻找的B_ID。那时,我想访问该部分中的所有叶子。
I.e.寻找图书馆2,书1,标题
注意:可能有更好的方法。
What i am looking to do is iterate through the libraries. When i find the L_ID that i am looking for, iterate through the books until i find the B_ID i'm looking for. At that point, i'd like to access all the leaves in that section. I.e. looking for library 2, book 1, title Note: There's likely a better way than this.
boost::property_tree::ptree libraries = config_.get_child("Library");
for (const auto &lib : libraries)
{
if (lib.second.get<uint16_6>("L_ID") == 2)
{
//at this point, i know i'm the correct library...
boost::property_tree::ptree books = lib.get_child("Book");
for (const auto &book : books)
{
if (book.second.get<uint16_t>("B_ID") == 1)
{
std::string mybook = book.second.get<std::string>("Title");
}
}
}
我尽快失败我试着看看我的第一个子树。这里出了什么问题?
I fail out as soon as i try looking into my first sub tree. What's going wrong here??
推荐答案
对于初学者来说,JSON存在严重缺陷。至少修复丢失的引号和逗号:
For starters, the "JSON" is wildly flawed. At least fix the missing quotes and commas:
{
"Library": {
"L_ID": "1",
"Book": {
"B_ID": "1",
"Title": "Moby Dick"
},
"Book": {
"B_ID": "2",
"Title": "Jurassic Park"
}
},
"Library": {
"L_ID": "2",
"Book": {
"B_ID": "1",
"Title": "Velocity"
},
"Book": {
"B_ID": "2",
"Title": "Creeper"
}
}
}
接下来,您似乎感到困惑。 get_child(Library)
获取该名称的第一个子节点,而不是包含名为Library的子节点的节点(顺便说一下,它将是根节点)。
Next up, you seem to be confused. get_child("Library")
gets the first child by that name, not a node containing child nodes called "Library" (that would be the root node, by the way).
我可以建议添加一些抽象,也许还有一些设施可以通过某些名称/属性来查询:
May I suggest adding some abstraction, and perhaps some facilities to query by some names/properties:
int main() {
Config cfg;
{
std::ifstream ifs("input.txt");
read_json(ifs, cfg.data_);
}
std::cout << "Book title: " << cfg.library(2).book(1).title() << "\n";
}
如您所见,我们假设 Config
可以找到库的类型:
As you can see, we assume a Config
type that can find a library:
Library library(Id id) const {
for (const auto& lib : libraries())
if (lib.id() == id) return lib;
throw std::out_of_range("library");
}
什么是 libraries()
?我们将深入研究它,但让我们看一下它:
What is libraries()
? We'll delve into it deeper, but lets just look at it for a second:
auto libraries() const {
using namespace PtreeTools;
return data_ | named("Library") | having("L_ID") | as<Library>();
}
这个魔法应该被理解为给我所有名为Library的节点,它具有L_ID属性,但将它们包装在Library对象中。暂时删除细节,让我们看一下库
对象,它显然知道 books()
:
That magic should be read as "give me all nodes that are named Library, which have a L_ID property but wrap them in a Library object". Skipping on the detail for now, lets look at the Library
object, which apparently knows about books()
:
struct Library {
ptree const& data_;
Id id() const { return data_.get<Id>("L_ID"); }
auto books() const {
using namespace PtreeTools;
return data_ | named("Book") | having("B_ID") | as<Book>();
}
Book book(Id id) const {
for (const auto& book : books())
if (book.id() == id) return book;
throw std::out_of_range("book");
}
};
我们在 books()
和 book(id)
查找特定项目。
We see the same pattern in books()
and book(id)
to find a specific item.
魔法使用Boost Range适配器并潜伏在 PtreeTools
:
The magic uses Boost Range adaptors and lurks in PtreeTools
:
namespace PtreeTools {
namespace detail {
// ...
}
auto named(std::string const& name) {
return detail::filtered(detail::KeyName{name});
}
auto having(std::string const& name) {
return detail::filtered(detail::HaveProperty{name});
}
template <typename T>
auto as() {
return detail::transformed(detail::As<T>{});
}
}
这看起来很简单,对。这是因为我们站在Boost Range的肩膀上:
That's deceptively simple, right. That's because we're standing on the shoulders of Boost Range:
namespace detail {
using boost::adaptors::filtered;
using boost::adaptors::transformed;
接下来,我们只定义知道如何过滤特定ptree节点的谓词:
Next, we only define the predicates that know how to filter for a specific ptree node:
using Value = ptree::value_type;
struct KeyName {
std::string const _target;
bool operator()(Value const& v) const {
return v.first == _target;
}
};
struct HaveProperty {
std::string const _target;
bool operator()(Value const& v) const {
return v.second.get_optional<std::string>(_target).is_initialized();
}
};
还有一个项目转换为我们的包装器对象:
And one transformation to project to our wrapper objects:
template <typename T>
struct As {
T operator()(Value const& v) const {
return T { v.second };
}
};
}
全部现场演示
#include <boost/property_tree/json_parser.hpp>
#include <boost/range/adaptors.hpp>
using boost::property_tree::ptree;
namespace PtreeTools {
namespace detail {
using boost::adaptors::filtered;
using boost::adaptors::transformed;
using Value = ptree::value_type;
struct KeyName {
std::string const _target;
bool operator()(Value const& v) const {
return v.first == _target;
}
};
struct HaveProperty {
std::string const _target;
bool operator()(Value const& v) const {
return v.second.get_optional<std::string>(_target).is_initialized();
}
};
template <typename T>
struct As {
T operator()(Value const& v) const {
return T { v.second };
}
};
}
auto named(std::string const& name) {
return detail::filtered(detail::KeyName{name});
}
auto having(std::string const& name) {
return detail::filtered(detail::HaveProperty{name});
}
template <typename T>
auto as() {
return detail::transformed(detail::As<T>{});
}
}
struct Config {
ptree data_;
using Id = uint16_t;
struct Book {
ptree const& data_;
Id id() const { return data_.get<Id>("B_ID"); }
std::string title() const { return data_.get<std::string>("Title"); }
};
struct Library {
ptree const& data_;
Id id() const { return data_.get<Id>("L_ID"); }
auto books() const {
using namespace PtreeTools;
return data_ | named("Book") | having("B_ID") | as<Book>();
}
Book book(Id id) const {
for (const auto& book : books())
if (book.id() == id) return book;
throw std::out_of_range("book");
}
};
auto libraries() const {
using namespace PtreeTools;
return data_ | named("Library") | having("L_ID") | as<Library>();
}
Library library(Id id) const {
for (const auto& lib : libraries())
if (lib.id() == id) return lib;
throw std::out_of_range("library");
}
};
#include <iostream>
int main() {
Config cfg;
{
std::ifstream ifs("input.txt");
read_json(ifs, cfg.data_);
}
std::cout << "Book title: " << cfg.library(2).book(1).title() << "\n";
}
打印:
Book title: Velocity
这篇关于通过多级提升树迭代的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!