简体   繁体   English

遍历多级提升树

[英]Iterate through multilevel boost tree

With my tree looking like this: 我的树看起来像这样:

{
"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"
     }
}
}

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. 当我找到要查找的L_ID时,请遍历书籍,直到找到要查找的B_ID。 At that point, i'd like to access all the leaves in that section. 到那时,我想访问该部分中的所有叶子。 Ie looking for library 2, book 1, title Note: There's likely a better way than this. 即寻找图书馆2,书1,书名注意:可能有比这更好的方法。

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?? 这是怎么了?

For starters, the "JSON" is wildly flawed. 对于初学者来说,“ JSON”存在严重缺陷。 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"
        }
    }
}

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). get_child("Library")通过该名称获取第一个子节点,而不是包含称为“ Library”的子节点的节点(顺便说一句,它将是根节点)。

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

As you can see, we assume a Config type that can find a library: 如您所见,我们假设可以找到一个库的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() ? 什么是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>();
}

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". 魔术应该理解为“给我所有名为Library的节点,这些节点具有L_ID属性,但将它们包装在Library对象中”。 Skipping on the detail for now, lets look at the Library object, which apparently knows about books() : 现在跳过细节,让我们看一下显然知道关于 books()Library对象:

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

We see the same pattern in books() and book(id) to find a specific item. 我们在books()book(id)看到了相同的模式,以查找特定项目。

The Magic 魔术

The magic uses Boost Range adaptors and lurks in PtreeTools : 魔术在PtreeTools使用Boost Range适配器和潜伏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>{});
    }
}

That's deceptively simple, right. 这看似简单,对。 That's because we're standing on the shoulders of Boost Range: 那是因为我们站在Boost Range的肩膀上:

namespace detail {
    using boost::adaptors::filtered;
    using boost::adaptors::transformed;

Next, we only define the predicates that know how to filter for a specific ptree node: 接下来,我们仅定义知道如何过滤特定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();
        }
    };

And one transformation to project to our wrapper objects: 以及一种转换为投影到我们的包装对象的转换:

    template <typename T>
        struct As {
            T operator()(Value const& v) const {
                return T { v.second };
            }
        };
}

Full Live Demo 完整现场演示

Live On Coliru 生活在Coliru

#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

@Sehe fixed your JSON to be syntactically correct, but I think it would make sense to go a bit further than that. @Sehe修复了您的JSON,使其在语法上正确无误,但我认为将其做得更有意义。 Given the data you're representing, it would make a great deal more sense to have an array of libraries, each of which contains an array of books, giving data something like this: 鉴于你代表的数据,它会成为一位伟大的交易更有意义库的数组,每个都包含书籍数组,给数据是这样的:

{
    "Libraries": [
        {
            "L_ID": 1,
            "Books": [
                {
                    "B_ID": 1,
                    "Title": "Moby Dick"
                },
                {
                    "B_ID": 2,
                    "Title": "Jurassic Park"
                }
            ]
        },
        {
            "L_ID": 2,
            "Books": [
                {
                    "B_ID": 1,
                    "Title": "Velocity"
                },
                {
                    "B_ID": 2,
                    "Title": "Creeper"
                }
            ]
        }
    ]
}

Then, if at all possible, I'd choose a library that's actually suited to the job at hand. 然后,如果可能的话,我会选择一个实际上适合手头工作的库。 Boost property tree isn't really intended as a general-purpose JSON library. Boost属性树并不是真正用作通用JSON库。 You can push it into that role if you really insist, but at least for what you've outlined in the question, it puts you through quite a bit of extra work to get what you want. 如果您真的坚持的话,可以将其提升为该角色,但是至少对于问题中概述的内容,它使您付出了很多额外的工作来获得所需的东西。

Personally, I'd probably use nlohmann's JSON library instead. 就个人而言,我可能会改用nlohmann的JSON库 Using it, we can proceed a little more directly to a solution. 使用它,我们可以更直接地进行解决。 Basically, once it's parsed a JSON file, we can treat the result a great deal like we would normal collections from the standard library--we can use all the normal algorithms, range-based for loops, and so on. 基本上,一旦解析了JSON文件,我们就可以像对待标准库中的普通集合一样对待结果了—我们可以使用所有普通算法,基于范围的for循环等等。 So, we can load a JSON file something like: 因此,我们可以加载类似以下内容的JSON文件:

using json=nlohmann::json;

std::ifstream in("libraries.json");
json lib_data;

in >> lib_data;

Then we can look through the libraries for a particular ID number with code something like this: 然后,我们可以使用以下代码在库中查找特定的ID号:

for (auto const &lib : libs["Libraries"])
    if (lib["L_ID"] == lib_num) 
        // we've found the library we want

Looking for a particular book is nearly identical: 寻找特定书籍几乎相同:

for (auto const &book : lib["Books"])
   if (book["B_ID"] == book_num)
       // we've found the book we want

From there, printing out the title looks something like: std::cout << book["Title"]; 从那里开始,打印标题如下: std::cout << book["Title"];

Putting those together, we could end up with code something like this: 将它们放在一起,我们可能最终得到如下代码:

#include "json.hpp"
#include <fstream>
#include <iostream>

using json = nlohmann::json;

std::string find_title(json lib_data, int lib_num, int book_num) {
    for (auto const &lib : lib_data["Libraries"])
        if (lib["L_ID"] == lib_num) 
            for (auto const &book : lib["Books"])
                if (book["B_ID"] == book_num)
                    return book["Title"];

    return "";
}

int main() {

    std::ifstream in("libraries.json");

    json lib_data;

    in >> lib_data;

    std::cout << find_title(lib_data, 1, 2);
}

If you really want to convert each JSON object into a C++ object, you can do that fairly easily as well. 如果您确实希望将每个JSON对象转换为C ++对象,则也可以轻松地做到这一点。 It would look something like this: 它看起来像这样:

namespace library_stuff {
    struct Book {
        int B_ID;
        std::string title;    
    };

    void from_json(json &j, Book &b) {
        b.B_ID = j["B_ID"];
        b.title = j["Title"];
    }
}

So there are two points here: you must name the function from_json , and it must be defined in the same namespace as the struct/class it's associated with. 因此,这里有两点:您必须将函数命名为from_json ,并且必须在与其关联的struct / class相同的名称空间中对其进行定义。 With this bit of scaffolding in place, we can convert from JSON to struct with a simple assignment, something like this: 有了这些脚手架,我们可以通过简单的分配将JSON转换为struct,如下所示:

book b = lib_data["Libraries"][0]["Books"][1];

I'd consider it highly questionable whether that's really useful in this particular case though. 我认为这在这种特殊情况下是否真的有用是非常值得怀疑的。

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

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