Итерировать по дочерним элементам PTree без включения имени тега - PullRequest
1 голос
/ 06 августа 2020

Я хотел бы знать, есть ли способ получить данные в дочерних узлах, перебирая вектор родительских узлов. У меня есть файл XML, который я планирую часто менять, поэтому я бы хотел избежать жесткого кодирования имен атрибутов. Таким образом, я хочу извлекать данные из моих дочерних узлов без указания имени тега узлов, используя pt.get_child(myparentNodes). Это у меня в основном.

Любая помощь приветствуется!

vector<string> parentNodes;
ptree pt;
ifstream fileName("myxml");
read_xml(fileName, pt);

for(const ptree::value_type &parent : pt)
{
    cout << parent.first << std::endl;
    parentNodes.push_back(parent.first);
  
}
 for(int i=0; i<parentNodes.size();i++)
 {
BOOST_FOREACH(boost::property_tree::ptree::value_type const &node,pt.get_child(parentNodes[i]))

/* I'm having trouble properly accessing the children nodes here */

1 Ответ

1 голос
/ 07 августа 2020

В вашем фрагменте (немного очищенном):

std::vector<std::string> parentNodes;
for(auto const& parent : pt) {
    std::cout << parent.first << std::endl;
    parentNodes.push_back(parent.first);
}

, похоже, собирает имена узлов дерева в parentNodes. Однако это предполагает, что имена уникальны или непусты.

Имена свойств не обязательно должны быть уникальными, и они не обязательно должны быть непустыми. Фактически массивы в дереве свойств часто моделируются как узлы только с безымянными дочерними узлами.

Тогда у вас возникли проблемы с получением дочерних узлов соответствующих узлов. Давайте сначала сделаем это утомительным способом (опять же, предполагая, что имена уникальны):

for (size_t i = 0; i < parentNodes.size(); i++) {
    auto& parent = pt.get_child(parentNodes[i]);

    for (auto const& child : parent) {
        std::cout << child.first << std::endl;
    }
}

Конечно, использование дальнего боя намного проще:

for (auto const& name : parentNodes) {
    auto& parent = pt.get_child(name);

    for (auto const& child : parent) {
        std::cout << child.first << std::endl;
    }
}

Еще лучше

Вы можете избежать предположений относительно именования, а также второго l oop и векторного хранилища:

for (auto const& parent : pt) {
    std::cout << parent.first << std::endl;

    auto& node = parent.second;
    for (auto const& child : node) {
        std::cout << child.first << std::endl;
    }
}

Это потому, что итератор указывает на пару (key, value). Фактически, в недавнем компиляторе вы можете написать l oop со структурированными привязками:

for (auto const& [name, node] : pt) {
    std::cout << name << std::endl;

    for (auto const& child : node) {
        std::cout << child.first << std::endl;
    }
}

, все еще делая то же самое.

Generalizing

Вы сказали, что хотите чтобы сделать этот generi c. Однако предположение о двухуровневой иерархии родительских / дочерних отношений не кажется мне "generi c". Я связал вас с некоторыми примерами, которые показывают общий обход c (например, поиск шаблонов по всему дереву) в последний раз , например Итерация по xml файлу с повышением - функция из этого примера:

Live On Wandbox

#include <boost/property_tree/xml_parser.hpp>
#include <iostream>

using boost::property_tree::ptree;
static auto settings = boost::property_tree::xml_writer_make_settings<std::string>(' ', 4);

template <typename Out>
Out enumerate_nodes(ptree const& pt, ptree::path_type path, Out out) {
    if (path.empty())
        return out;

    if (path.single()) {
        auto name = path.reduce();
        for (auto& child : pt) {
            if (child.first == name)
                *out++ = child.second;
        }
    } else {
        auto head = path.reduce();
        for (auto& child : pt) {
            if (head == "*" || child.first == head) {
                out = enumerate_nodes(child.second, path, out);
            }
        }
    }

    return out;
}

int main() {
    std::ifstream fileName("input.xml");
    ptree pt;
    read_xml(fileName, pt);

    for (auto const& [name, node] : pt) {
        std::cout << name << std::endl;

        for (auto const& child : node)
            std::cout << child.first << std::endl;
    }

    std::vector<std::reference_wrapper<ptree const>> matched;
    enumerate_nodes(pt, "root.parent2.child3", back_inserter(matched));

    for (ptree const& match : matched)
        std::cout << "Matched: " << match.get_value<std::string>() << "\n";
}

При использовании input.xml:

<?xml version="1.0"?>
<root>
    <parent1>
        <child1>parent1/child1</child1>
        <child2>parent1/child2</child2>
        <child3>parent1/child3</child3>
        <child4>parent1/child4</child4>
    </parent1>
    <parent2>
        <child1>parent2/child1</child1>
        <child2>parent2/child2</child2>
        <child3>parent2/child3</child3>
        <child4>parent2/child4</child4>
    </parent2>
    <parent3>
        <child1>parent3/child1</child1>
        <child2>parent3/child2</child2>
        <child3>parent3/child3</child3>
        <child4>parent3/child4</child4>
    </parent3>
    <parent4>
        <child1>parent4/child1</child1>
        <child2>parent4/child2</child2>
        <child3>parent4/child3</child3>
        <child4>parent4/child4</child4>
    </parent4>
</root>

Печать

root
parent1
parent2
parent3
parent4
Matched: parent2/child3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...