首先,“JSON”存在严重缺陷。至少修复丢失的引号和逗号:
{
"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”的子节点的节点(顺便说一下,这将是根节点)。
我可以建议添加一些抽象,也许还可以添加一些通过某些名称/属性进行查询的工具:
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
可以找到库的类型:
Library library(Id id) const {
for (const auto& lib : libraries())
if (lib.id() == id) return lib;
throw std::out_of_range("library");
}
What is libraries()
?我们将更深入地研究它,但让我们先看一下它:
auto libraries() const {
using namespace PtreeTools;
return data_ | named("Library") | having("L_ID") | as<Library>();
}
这个魔法应该理解为“给我所有名为 Library 的节点,它们具有 L_ID 属性,但将它们包装在 Library 对象中”。现在先跳过细节,让我们看一下Library
对象,显然知道 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()
and book(id)
查找特定项目。
魔术
魔法使用升压范围适配器并潜伏在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 的肩膀上:
namespace detail {
using boost::adaptors::filtered;
using boost::adaptors::transformed;
接下来,我们只定义知道如何过滤特定 ptree 节点的谓词:
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 };
}
};
}
完整现场演示
Live On Coliru http://coliru.stacked-crooked.com/a/3d385e9d30ea6895
#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";
}
Prints:
Book title: Velocity